mirror of
https://github.com/Matir/skel.git
synced 2026-05-25 21:19:09 -07:00
Compare commits
457 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9770514d6a | ||
|
|
e1724c77f3 | ||
|
|
ecbc25e5ac | ||
|
|
ea33840ef6 | ||
|
|
1048775da3 | ||
|
|
c9af80bc5d | ||
|
|
d8b4991419 | ||
|
|
158d9f6e4e | ||
|
|
b1d1c42a02 | ||
|
|
f8ec9cc338 | ||
|
|
6e3c3dd269 | ||
|
|
77b8374871 | ||
|
|
4645682b5c | ||
|
|
75bdebb497 | ||
|
|
4e72b9b18c | ||
|
|
bd2c2287cd | ||
|
|
db2c02bd2d | ||
|
|
fec16225e4 | ||
|
|
fa6a878487 | ||
|
|
1804357162 | ||
|
|
202d871a59 | ||
|
|
467d916f33 | ||
|
|
6d2bfdbcea | ||
|
|
1d0a09c442 | ||
|
|
37c765ae29 | ||
|
|
41f8a49381 | ||
|
|
3f9c41d266 | ||
|
|
69e4b83652 | ||
|
|
74c2472dd2 | ||
|
|
ecc39344ca | ||
|
|
49a314e388 | ||
|
|
41293eb788 | ||
|
|
05798dcb67 | ||
|
|
4e1b263170 | ||
|
|
46c00c61be | ||
|
|
ce973d5bbf | ||
|
|
f326992306 | ||
|
|
7f76b24cb9 | ||
|
|
a5b08680c5 | ||
|
|
2510f1ad87 | ||
|
|
b6af18017b | ||
|
|
829f7ae1de | ||
|
|
6fd9769ccc | ||
|
|
758c59bc8e | ||
|
|
39e003f666 | ||
|
|
5b1bb1c233 | ||
|
|
2e0ebb4d6f | ||
|
|
31aeca1b73 | ||
|
|
7006974bb3 | ||
|
|
92fb8cb47d | ||
|
|
a46ee1f24c | ||
|
|
c1b565f2c5 | ||
|
|
0ded57fa46 | ||
|
|
f1495add30 | ||
|
|
a6146c2763 | ||
|
|
60f68f11a2 | ||
|
|
a1262e9fba | ||
|
|
d5a3ed41c6 | ||
|
|
295ef071ee | ||
|
|
313c16bdc9 | ||
|
|
fad65e2aaa | ||
|
|
00696b23fa | ||
|
|
1c0c5dd32b | ||
|
|
8cac819127 | ||
|
|
0ebc32e30a | ||
|
|
a0378fb583 | ||
|
|
3cad40f858 | ||
|
|
6bda0bb639 | ||
|
|
c1d0045f44 | ||
|
|
f50edc1fa6 | ||
|
|
9ab1f9c298 | ||
|
|
cdbc40d1e8 | ||
|
|
487dbe3751 | ||
|
|
aea7b0927e | ||
|
|
645b631afc | ||
|
|
b631471b0c | ||
|
|
fb7636f5f1 | ||
|
|
20a5885031 | ||
|
|
21b24b2c2f | ||
|
|
122dc2075b | ||
|
|
c0fd99afd6 | ||
|
|
0ebb331cb3 | ||
|
|
bddd4ed107 | ||
|
|
a92b05a51c | ||
|
|
5a49dd95b2 | ||
|
|
d9223c92dc | ||
|
|
4c9038c33d | ||
|
|
9a04b847ec | ||
|
|
c1ef9f5e7a | ||
|
|
5a4d04ea43 | ||
|
|
f4e3447eb7 | ||
|
|
ff0a7d150d | ||
|
|
657d30c381 | ||
|
|
5cc08e472e | ||
|
|
89ae97145e | ||
|
|
d50faa1ec6 | ||
|
|
243da55c86 | ||
|
|
0e491395da | ||
|
|
b7cbf90ac7 | ||
|
|
9e1ad38f94 | ||
|
|
821c2f8b80 | ||
|
|
4e3b466044 | ||
|
|
fe5b74511f | ||
|
|
f80b029ae3 | ||
|
|
d4a4d8ee19 | ||
|
|
375ff434f2 | ||
|
|
e270bac5f2 | ||
|
|
2b31d4d5d2 | ||
|
|
91920fef0e | ||
|
|
f2faccd93f | ||
|
|
dea3f289c2 | ||
|
|
61d00103bf | ||
|
|
2122c88e03 | ||
|
|
7064a5d5a1 | ||
|
|
ae344d6dba | ||
|
|
d7b6095c47 | ||
|
|
c7777ede0b | ||
|
|
6c2c12faeb | ||
|
|
7e9a87afdd | ||
|
|
d6cd16e9f0 | ||
|
|
3548113a7d | ||
|
|
f3e82690f8 | ||
|
|
d596d10678 | ||
|
|
0406395ea0 | ||
|
|
c9ed72a5a6 | ||
|
|
5e65608c39 | ||
|
|
acb13c6908 | ||
|
|
9b9330db45 | ||
|
|
4743b635aa | ||
|
|
bbeb451251 | ||
|
|
5057ab890b | ||
|
|
ee7f78e99c | ||
|
|
0b5ca1cefc | ||
|
|
ab2f62345f | ||
|
|
05df603332 | ||
|
|
ca71dea284 | ||
|
|
00bbe65691 | ||
|
|
de8d2cf608 | ||
|
|
a81e6e9e8d | ||
|
|
52d05c400d | ||
|
|
83b7adf1ca | ||
|
|
f6d7f22a77 | ||
|
|
746653d28d | ||
|
|
695cef6cb3 | ||
|
|
e0eebe2641 | ||
|
|
d5024a536d | ||
|
|
d10f82d088 | ||
|
|
6ffae42e33 | ||
|
|
5658c61627 | ||
|
|
e227b65d6d | ||
|
|
569199a280 | ||
|
|
61e81f33c8 | ||
|
|
422c56139f | ||
|
|
15c118583a | ||
|
|
31c2783286 | ||
|
|
1131b46e44 | ||
|
|
fecdf1f67e | ||
|
|
38c21397cf | ||
|
|
b2eff8284c | ||
|
|
ef902daa1c | ||
|
|
42361045bc | ||
|
|
a45afefca4 | ||
|
|
d40eb6b0a7 | ||
|
|
2c26051817 | ||
|
|
b7b16e20a6 | ||
|
|
f6573e30b2 | ||
|
|
43e8661036 | ||
|
|
55823417eb | ||
|
|
eab08b1c95 | ||
|
|
560f803455 | ||
|
|
c6fe0ff1c8 | ||
|
|
05c910e675 | ||
|
|
efe093471c | ||
|
|
e99d20e47e | ||
|
|
a3fb4c5e66 | ||
|
|
f9a48c70c4 | ||
|
|
d072e3a5dd | ||
|
|
ce13fbc610 | ||
|
|
45be3e45f1 | ||
|
|
442127f7c1 | ||
|
|
87797ca803 | ||
|
|
7c1c5cfbd7 | ||
|
|
6a9358cd21 | ||
|
|
a195f0310d | ||
|
|
11ecbdaf04 | ||
|
|
bc9150b592 | ||
|
|
f81410cfc0 | ||
|
|
87239b2034 | ||
|
|
50011b1675 | ||
|
|
eb3392966a | ||
|
|
3188effbd3 | ||
|
|
80b119f6ad | ||
|
|
e87b4cb3d4 | ||
|
|
0c08d4ebe7 | ||
|
|
a7d59e971f | ||
|
|
233b3e7a7b | ||
|
|
691f64a477 | ||
|
|
670875f227 | ||
|
|
e90c014d88 | ||
|
|
d76a278a95 | ||
|
|
8eff002d55 | ||
|
|
3015e7a1c0 | ||
|
|
a286795570 | ||
|
|
99e8ff6f2b | ||
|
|
4a77c0e900 | ||
|
|
35f494c3d4 | ||
|
|
f650d12efb | ||
|
|
2c67b58ba6 | ||
|
|
ba9534577a | ||
|
|
193b22e0d1 | ||
|
|
b7353bd34e | ||
|
|
12b2ed211b | ||
|
|
ee5a78b578 | ||
|
|
25a9194480 | ||
|
|
f4ea05681c | ||
|
|
eb193fe1de | ||
|
|
e02dc0be80 | ||
|
|
6c3dea3721 | ||
|
|
cb3ba03c49 | ||
|
|
d7cc6fd508 | ||
|
|
b628453b10 | ||
|
|
157e6ee20e | ||
|
|
e375d7f472 | ||
|
|
373189f440 | ||
|
|
a719ca9972 | ||
|
|
4937a98ccc | ||
|
|
81093ab3d6 | ||
|
|
ab85cb5ecf | ||
|
|
cbb580fcdf | ||
|
|
aea9e50141 | ||
|
|
10ce6ff7f6 | ||
|
|
cd9bb5d6cc | ||
|
|
5cfbc4e88a | ||
|
|
44d981e3c3 | ||
|
|
3f509e17c1 | ||
|
|
af3365ad8e | ||
|
|
6888e8503f | ||
|
|
e52af2d21a | ||
|
|
a5a5c7d5df | ||
|
|
aca274d7df | ||
|
|
70cdfcdc07 | ||
|
|
9a38b456d7 | ||
|
|
a4196d7bc1 | ||
|
|
cbcc6e6fb6 | ||
|
|
f2ca693f7b | ||
|
|
da43e7533d | ||
|
|
9c9c56b030 | ||
|
|
747bc7becd | ||
|
|
c3c9714b8d | ||
|
|
5865f5130f | ||
|
|
6803ac2ce2 | ||
|
|
c512401acd | ||
|
|
dae254e240 | ||
|
|
2ab9bd3452 | ||
|
|
b68c1e2c65 | ||
|
|
3c8d8ca5e1 | ||
|
|
f6e6c42b26 | ||
|
|
7165e7c93f | ||
|
|
63f401c84b | ||
|
|
dea863ee35 | ||
|
|
d01a320de7 | ||
|
|
0564dbbb3d | ||
|
|
1efa61a9f4 | ||
|
|
77a01d2fda | ||
|
|
71711a5cc0 | ||
|
|
a49474fe60 | ||
|
|
2217cfb47b | ||
|
|
75b0c5db9e | ||
|
|
50174a4f72 | ||
|
|
04470627c6 | ||
|
|
e6318c4cc1 | ||
|
|
15b575c34d | ||
|
|
2db123c772 | ||
|
|
2de511f5f3 | ||
|
|
890a24ec07 | ||
|
|
a3aaed6c0c | ||
|
|
9f4fbfb06a | ||
|
|
273d6b495f | ||
|
|
d41703820d | ||
|
|
5748d014f1 | ||
|
|
2779322b24 | ||
|
|
50948acee4 | ||
|
|
670cf5c8dc | ||
|
|
d213b3b234 | ||
|
|
37d606cc49 | ||
|
|
8f57fad78c | ||
|
|
f8ddb56b02 | ||
|
|
1f59627da9 | ||
|
|
2c88a59188 | ||
|
|
9ec67f4c1c | ||
|
|
db3193e0bc | ||
|
|
9c83b8fc40 | ||
|
|
3da887ffad | ||
|
|
f56b7d0b7b | ||
|
|
e18e66391b | ||
|
|
8b66586fe8 | ||
|
|
ac4109cee6 | ||
|
|
99af2287c5 | ||
|
|
6adaaffabd | ||
|
|
c6f217d47c | ||
|
|
733093ee7a | ||
|
|
441a533e2a | ||
|
|
8d30879224 | ||
|
|
f94a4dc67c | ||
|
|
0bb950b053 | ||
|
|
4ec8f22ab8 | ||
|
|
ba0c734b06 | ||
|
|
f24e3ebb24 | ||
|
|
4af40de24b | ||
|
|
351b136cbc | ||
|
|
a5b0677903 | ||
|
|
ebf5c1cd22 | ||
|
|
94abfa2fe2 | ||
|
|
15f5f91e2a | ||
|
|
2b6229acc3 | ||
|
|
e05a6aa473 | ||
|
|
673fca3c05 | ||
|
|
f6a7b372b7 | ||
|
|
685fb22dd3 | ||
|
|
e788dedf69 | ||
|
|
a482092fdb | ||
|
|
b3eb8a8e44 | ||
|
|
3afee0505b | ||
|
|
50b74fc06e | ||
|
|
74bf253950 | ||
|
|
434120cc90 | ||
|
|
97e23b3669 | ||
|
|
f9f3dd05c8 | ||
|
|
6e095d9fbc | ||
|
|
c25540a398 | ||
|
|
fae178e8ac | ||
|
|
15ea12d54b | ||
|
|
291de7b2c1 | ||
|
|
bbba28e166 | ||
|
|
27dc8fb22c | ||
|
|
a5f55034fa | ||
|
|
062da36145 | ||
|
|
9e3b929a4b | ||
|
|
3170a41c4b | ||
|
|
b37f95383c | ||
|
|
e6e66b0d05 | ||
|
|
6e6b63cb28 | ||
|
|
20bb942ac6 | ||
|
|
d256cdf12b | ||
|
|
c20739472f | ||
|
|
66e6833623 | ||
|
|
bc5f9a2741 | ||
|
|
04d4ab4d25 | ||
|
|
ce1d4780b6 | ||
|
|
8e79910857 | ||
|
|
999ef09790 | ||
|
|
2f9841e2ac | ||
|
|
2879f63aff | ||
|
|
79a3e1f46c | ||
|
|
62392e3712 | ||
|
|
9717fef420 | ||
|
|
3c83cb1761 | ||
|
|
2a71557663 | ||
|
|
b6a2b65704 | ||
|
|
398e39b9eb | ||
|
|
f6b5cd90fc | ||
|
|
1d14f294eb | ||
|
|
f3cb3607db | ||
|
|
d117ee6b2e | ||
|
|
7e249843a6 | ||
|
|
465bfd37f1 | ||
|
|
5ad1ad4616 | ||
|
|
e82cad68e1 | ||
|
|
5e372e2b35 | ||
|
|
9e392af7d1 | ||
|
|
68bcf536cb | ||
|
|
698f2defa9 | ||
|
|
a42128f5c9 | ||
|
|
6559acbf90 | ||
|
|
a654a7747c | ||
|
|
d158a0a373 | ||
|
|
229d5353e3 | ||
|
|
20389cba90 | ||
|
|
aaf56b794d | ||
|
|
827665279a | ||
|
|
8467bfce20 | ||
|
|
116ae13896 | ||
|
|
0f08f9710a | ||
|
|
9c147af8c1 | ||
|
|
5e498f9bcd | ||
|
|
1cab41bfd5 | ||
|
|
3e225ae2ab | ||
|
|
cc15b17e28 | ||
|
|
7aa9e57a59 | ||
|
|
530b205b3d | ||
|
|
0bed0a80a3 | ||
|
|
244c5da0e7 | ||
|
|
e885afbff9 | ||
|
|
8a0e4cd774 | ||
|
|
5cf313140c | ||
|
|
f0c3ab8753 | ||
|
|
8cd3682a23 | ||
|
|
ee83a40686 | ||
|
|
7f9a3774ae | ||
|
|
8d7c76b227 | ||
|
|
0a92dd0ed7 | ||
|
|
eae69fe414 | ||
|
|
dcef7445b2 | ||
|
|
11d1086a68 | ||
|
|
a88d120b3c | ||
|
|
15479f3829 | ||
|
|
9178794de5 | ||
|
|
fc120b66fe | ||
|
|
57b181bd74 | ||
|
|
aa6a2e806d | ||
|
|
eec3c9ba7a | ||
|
|
d67b18f2ea | ||
|
|
72b82ff2e3 | ||
|
|
6492beb726 | ||
|
|
08142409f8 | ||
|
|
8225785dcc | ||
|
|
c7c97b84c5 | ||
|
|
f9ce8b3df8 | ||
|
|
69c1184b58 | ||
|
|
7e432287ac | ||
|
|
50cb109c99 | ||
|
|
c915b20450 | ||
|
|
f3ea935dd5 | ||
|
|
a6414425b7 | ||
|
|
ad73f12103 | ||
|
|
7d35ace5bb | ||
|
|
e4f2cc94b5 | ||
|
|
fcbe2e781b | ||
|
|
7a5abd73cb | ||
|
|
07926d6673 | ||
|
|
24690e8399 | ||
|
|
2232dce811 | ||
|
|
f6de62700a | ||
|
|
01272f4474 | ||
|
|
e176b0c136 | ||
|
|
67fad831e5 | ||
|
|
048d7fb1ba | ||
|
|
24c1c8edfb | ||
|
|
614adeed08 | ||
|
|
12a92703f0 | ||
|
|
288a9c3a7e | ||
|
|
2419693164 | ||
|
|
b8607f4bdc | ||
|
|
5c2289e283 | ||
|
|
deb8b62c12 | ||
|
|
8e984d3c3b | ||
|
|
eb0206575a | ||
|
|
44d273dd7c | ||
|
|
8029afc15a | ||
|
|
f5766e7661 | ||
|
|
88c096cd95 | ||
|
|
3005abff34 | ||
|
|
8b9c9581e4 | ||
|
|
9d85ae77fa | ||
|
|
693b349024 | ||
|
|
a82e0cdb37 | ||
|
|
80d0b2844e |
6
.Brewfile.ignore
Normal file
6
.Brewfile.ignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Add package names here to ignore them in Brewfile updates.
|
||||||
|
# One package per line.
|
||||||
|
# Example:
|
||||||
|
# iterm2
|
||||||
|
# wget
|
||||||
|
orbstack
|
||||||
5
.gemini/settings.json
Normal file
5
.gemini/settings.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"context": {
|
||||||
|
"fileName": "AGENTS.md"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
.git-crypt/.gitattributes
vendored
3
.git-crypt/.gitattributes
vendored
@@ -1,3 +0,0 @@
|
|||||||
# Do not edit this file. To specify the files to encrypt, create your own
|
|
||||||
# .gitattributes file in the directory where your files are.
|
|
||||||
* !filter !diff
|
|
||||||
Binary file not shown.
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1 +0,0 @@
|
|||||||
private_dotfiles/** filter=git-crypt diff=git-crypt
|
|
||||||
|
|||||||
80
.githooks/githooks.sh
Executable file
80
.githooks/githooks.sh
Executable file
@@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# --- 1. Identity Resolution ---
|
||||||
|
# Finds the physical location of this script, regardless of symlinks
|
||||||
|
REAL_PATH=$(realpath "$0")
|
||||||
|
REAL_NAME=$(basename "$REAL_PATH")
|
||||||
|
HOOKS_DIR=$(dirname "$REAL_PATH")
|
||||||
|
CALLED_AS=$(basename "$0")
|
||||||
|
|
||||||
|
# --- 2. Self-Installation / Sync Logic ---
|
||||||
|
if [ "$CALLED_AS" == "$REAL_NAME" ]; then
|
||||||
|
echo "🔧 Synchronizing Git Hook Dispatcher..."
|
||||||
|
|
||||||
|
# Point Git to this directory
|
||||||
|
git config core.hooksPath "$HOOKS_DIR"
|
||||||
|
|
||||||
|
# Create symlinks for any [hook-name].d directories found
|
||||||
|
for d in "$HOOKS_DIR"/*.d/; do
|
||||||
|
[ -d "$d" ] || continue
|
||||||
|
HOOK_NAME=$(basename "$d" .d)
|
||||||
|
TARGET="$HOOKS_DIR/$HOOK_NAME"
|
||||||
|
|
||||||
|
if [ ! -L "$TARGET" ]; then
|
||||||
|
ln -sf "$REAL_NAME" "$TARGET"
|
||||||
|
chmod +x "$TARGET"
|
||||||
|
echo " ✨ Linked: $HOOK_NAME"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Cleanup: Remove symlinks that no longer have a matching .d directory
|
||||||
|
for link in "$HOOKS_DIR"/*; do
|
||||||
|
if [ -L "$link" ] && [ "$(basename "$link")" != "$REAL_NAME" ]; then
|
||||||
|
if [ ! -d "${link}.d" ]; then
|
||||||
|
rm "$link"
|
||||||
|
echo " 🗑️ Removed: $(basename "$link")"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "✅ Done."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 3. Selective Stdin Buffering ---
|
||||||
|
# Buffer stdin only for hooks that expect it to prevent hanging/performance hits
|
||||||
|
STDIN_DATA=""
|
||||||
|
case "$CALLED_AS" in
|
||||||
|
pre-push|post-rewrite|pre-receive|post-receive|reference-transaction)
|
||||||
|
# Check if stdin has data (is not a terminal)
|
||||||
|
if [ ! -t 0 ]; then
|
||||||
|
STDIN_DATA=$(cat)
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- 4. Dispatch Logic ---
|
||||||
|
SUB_HOOK_DIR="${HOOKS_DIR}/${CALLED_AS}.d"
|
||||||
|
|
||||||
|
if [ -d "$SUB_HOOK_DIR" ]; then
|
||||||
|
# Sort files naturally so 01- runs before 02-
|
||||||
|
for script in $(ls "$SUB_HOOK_DIR" | sort); do
|
||||||
|
FULL_PATH="$SUB_HOOK_DIR/$script"
|
||||||
|
[ -x "$FULL_PATH" ] || continue
|
||||||
|
|
||||||
|
# Replay stdin if we captured it, otherwise execute normally
|
||||||
|
if [ -n "$STDIN_DATA" ]; then
|
||||||
|
echo "$STDIN_DATA" | "$FULL_PATH" "$@"
|
||||||
|
else
|
||||||
|
"$FULL_PATH" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Exit immediately if any sub-script fails
|
||||||
|
EXIT_CODE=$?
|
||||||
|
if [ $EXIT_CODE -ne 0 ]; then
|
||||||
|
echo "❌ Hook '$CALLED_AS' failed at: $script"
|
||||||
|
exit $EXIT_CODE
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
1
.githooks/pre-commit
Symbolic link
1
.githooks/pre-commit
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
githooks.sh
|
||||||
21
.githooks/pre-commit.d/05-check-brewfile
Executable file
21
.githooks/pre-commit.d/05-check-brewfile
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Check if Brewfile needs updating
|
||||||
|
if [[ "$(uname)" != "Darwin" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# We use the script we just created
|
||||||
|
UPDATE_SCRIPT="bin/macos/update_brewfile"
|
||||||
|
|
||||||
|
if [[ -x "$UPDATE_SCRIPT" ]]; then
|
||||||
|
echo "🔍 Checking Brewfile synchronization..."
|
||||||
|
# Run in dry-run mode with --add-only and see if there's output
|
||||||
|
DIFF_OUTPUT=$("$UPDATE_SCRIPT" --dry-run --add-only 2>/dev/null)
|
||||||
|
if [[ "$DIFF_OUTPUT" == *"Changes detected"* ]]; then
|
||||||
|
echo "⚠️ Brewfile is out of sync with your installed packages."
|
||||||
|
echo " Run '$UPDATE_SCRIPT' to synchronize it."
|
||||||
|
echo ""
|
||||||
|
# We don't fail the commit, just warn.
|
||||||
|
fi
|
||||||
|
fi
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,3 +4,6 @@ installed-prefs
|
|||||||
*~
|
*~
|
||||||
*.bak
|
*.bak
|
||||||
local_dotfiles
|
local_dotfiles
|
||||||
|
dotfile_overlays/*
|
||||||
|
!dotfile_overlays/.keep
|
||||||
|
!dotfile_overlays/README
|
||||||
|
|||||||
32
.gitmodules
vendored
32
.gitmodules
vendored
@@ -1,31 +1 @@
|
|||||||
[submodule "dotfiles/vim/pack/matir/opt/solarized8"]
|
|
||||||
path = dotfiles/vim/pack/matir/opt/solarized8
|
|
||||||
url = https://github.com/lifepillar/vim-solarized8.git
|
|
||||||
fetchRecurseSubmodules = true
|
|
||||||
[submodule "dotfiles/vim/pack/matir/start/surround"]
|
|
||||||
path = dotfiles/vim/pack/matir/start/surround
|
|
||||||
url = https://github.com/tpope/vim-surround.git
|
|
||||||
fetchRecurseSubmodules = true
|
|
||||||
[submodule "dotfiles/vim/pack/matir/start/editorconfig"]
|
|
||||||
path = dotfiles/vim/pack/matir/start/editorconfig
|
|
||||||
url = https://github.com/editorconfig/editorconfig-vim.git
|
|
||||||
fetchRecurseSubmodules = true
|
|
||||||
[submodule "dotfiles/vim/pack/matir/start/fugitive"]
|
|
||||||
path = dotfiles/vim/pack/matir/start/fugitive
|
|
||||||
url = https://github.com/tpope/vim-fugitive
|
|
||||||
fetchRecurseSubmodules = true
|
|
||||||
[submodule "dotfiles/vim/pack/matir/start/ctrlp"]
|
|
||||||
path = dotfiles/vim/pack/matir/start/ctrlp
|
|
||||||
url = https://github.com/ctrlpvim/ctrlp.vim.git
|
|
||||||
fetchRecurseSubmodules = true
|
|
||||||
[submodule "dotfiles/vim/pack/matir/start/vim-go"]
|
|
||||||
path = dotfiles/vim/pack/matir/start/vim-go
|
|
||||||
url = https://github.com/fatih/vim-go.git
|
|
||||||
fetchRecurseSubmodules = true
|
|
||||||
[submodule "dotfiles/tmux/tmux-logging"]
|
|
||||||
path = dotfiles/tmux/tmux-logging
|
|
||||||
url = https://github.com/tmux-plugins/tmux-logging.git
|
|
||||||
fetchRecurseSubmodules = true
|
|
||||||
[submodule "dotfiles/vim/pack/matir/start/syntastic"]
|
|
||||||
path = dotfiles/vim/pack/matir/start/syntastic
|
|
||||||
url = https://github.com/vim-syntastic/syntastic.git
|
|
||||||
|
|||||||
70
AGENTS.md
Normal file
70
AGENTS.md
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
## Installation and Environment
|
||||||
|
|
||||||
|
This is a set of dotfiles and utilities for setting up my personal environment
|
||||||
|
on POSIX-style environments. It is cloned from github and installed from the
|
||||||
|
`install.sh` script.
|
||||||
|
|
||||||
|
It mostly relies on symlinking dotfiles and other resources into the appropriate
|
||||||
|
locations, but also installs dependencies in various ways.
|
||||||
|
|
||||||
|
I primarily target Debian Linux-based (Debian, Ubuntu, and Kali Linux) systems
|
||||||
|
as well as MacOS. Other platforms are lower priorities. Shell scripts ending
|
||||||
|
in `.sh` should use only POSIX features unless there is a shebang line at the
|
||||||
|
beginning suggesting a different shell will be used. In particular, those
|
||||||
|
in directories with names like bash might use those shells.
|
||||||
|
|
||||||
|
`zsh` and `fish` are the key interactive shells to be configured, but `bash`
|
||||||
|
may also be used at times.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
* `bin/`: Contains executable scripts symlinked to `~/bin/`. Subdirectories like `macos/`, `restic/`, and `setup/` are included.
|
||||||
|
* `dotfiles/`: Contains configuration files (dotfiles) symlinked to the home directory.
|
||||||
|
* `dotfile_overlays/`: Each directory within is symlinked to the home directory, allowing for modular or git-submodule-based configurations.
|
||||||
|
* `local_dotfiles/`: If present, its contents are symlinked to the home directory (ignored by git).
|
||||||
|
* `packages/`: Contains lists of packages (one per line) for different environments or toolsets.
|
||||||
|
* `keys/`: Contains SSH keys (`ssh/`), GPG keys (`gpg/`), and a `known_hosts` file to be installed/merged.
|
||||||
|
* `skeltools/`: Internal utilities used by the installation scripts.
|
||||||
|
* `sysctl/` and `udev/`: Linux system configuration files.
|
||||||
|
* `Brewfile`: Homebrew package list for macOS environments.
|
||||||
|
* `install.sh`: The primary installation script for symlinking and basic setup.
|
||||||
|
|
||||||
|
## Notes on Security Issues
|
||||||
|
|
||||||
|
It is safe to have scripts and tools re-invoke themselves with sudo when they
|
||||||
|
require elevated privileges, as these are my own. Do not attempt to remove
|
||||||
|
these use cases.
|
||||||
|
|
||||||
|
## Making Changes
|
||||||
|
|
||||||
|
**IMPORTANT**: Only make those changes which are explicitly requested. If you
|
||||||
|
identify other issues, notify me about them, but do not suggest changes until I
|
||||||
|
ask for them.
|
||||||
|
|
||||||
|
When making large changes, explain your chain of thought transparently and
|
||||||
|
explain solution design.
|
||||||
|
|
||||||
|
If making changes that affects how the user installs the tools, update
|
||||||
|
`README.md` accordingly.
|
||||||
|
|
||||||
|
### Adding a new dotfile
|
||||||
|
|
||||||
|
1. Place the new dotfile in the `dotfiles/` directory.
|
||||||
|
2. Alternatively, use `dotfile_overlays/` if the dotfile belongs to a specific group or submodule.
|
||||||
|
3. The `install.sh` script will automatically symlink it to the home directory.
|
||||||
|
|
||||||
|
### Adding a new script to `bin/`
|
||||||
|
|
||||||
|
1. Add the new script to the `bin/` directory (or an appropriate subdirectory).
|
||||||
|
2. Ensure the script is executable (`chmod +x`).
|
||||||
|
|
||||||
|
### Adding a new package
|
||||||
|
|
||||||
|
1. Identify the appropriate package list in the `packages/` directory (e.g., `packages/cli`, `packages/kali`).
|
||||||
|
2. Add the new package name to the list (one per line).
|
||||||
|
3. If a new package set is required, create a new file in the `packages/` directory.
|
||||||
|
4. For macOS-specific packages, also consider adding them to the `Brewfile`.
|
||||||
|
|
||||||
|
### Platform-specific changes
|
||||||
|
|
||||||
|
When making changes that are specific to a platform (e.g., Debian vs. macOS), please check for existing conventions in the `install.sh` script or other files. Use conditional logic (e.g., checking `uname`) to apply platform-specific settings.
|
||||||
131
Brewfile
Normal file
131
Brewfile
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
tap "dart-lang/dart"
|
||||||
|
tap "holtwick/tap"
|
||||||
|
tap "sass/sass"
|
||||||
|
|
||||||
|
brew "ack"
|
||||||
|
brew "acme.sh"
|
||||||
|
brew "age"
|
||||||
|
brew "autoconf"
|
||||||
|
brew "automake"
|
||||||
|
brew "b2-tools"
|
||||||
|
brew "bat"
|
||||||
|
brew "binwalk"
|
||||||
|
brew "cask"
|
||||||
|
brew "ccache"
|
||||||
|
brew "certbot"
|
||||||
|
brew "cloudflared"
|
||||||
|
brew "cmake"
|
||||||
|
brew "colima"
|
||||||
|
brew "devcontainer"
|
||||||
|
brew "dfu-util"
|
||||||
|
brew "difftastic"
|
||||||
|
brew "direnv"
|
||||||
|
brew "duck"
|
||||||
|
brew "dust"
|
||||||
|
brew "earthly"
|
||||||
|
brew "esptool"
|
||||||
|
brew "fish"
|
||||||
|
brew "fq"
|
||||||
|
brew "gh"
|
||||||
|
brew "ghidra", link: false
|
||||||
|
brew "git"
|
||||||
|
brew "git-delta"
|
||||||
|
brew "git-lfs"
|
||||||
|
brew "gnupg"
|
||||||
|
brew "go"
|
||||||
|
brew "gradle"
|
||||||
|
brew "hf"
|
||||||
|
brew "holtwick/tap/bx"
|
||||||
|
brew "htop"
|
||||||
|
brew "httpie"
|
||||||
|
brew "hugo"
|
||||||
|
brew "imagemagick"
|
||||||
|
brew "john-jumbo"
|
||||||
|
brew "jq"
|
||||||
|
brew "kubeconform"
|
||||||
|
brew "kubectx"
|
||||||
|
brew "librsvg"
|
||||||
|
brew "lima"
|
||||||
|
brew "minikube"
|
||||||
|
brew "mise"
|
||||||
|
brew "mosh"
|
||||||
|
brew "neovim"
|
||||||
|
brew "ninja"
|
||||||
|
brew "nmap"
|
||||||
|
brew "ollama"
|
||||||
|
brew "p7zip"
|
||||||
|
brew "pipenv"
|
||||||
|
brew "pipx"
|
||||||
|
brew "pkgconf"
|
||||||
|
brew "protobuf"
|
||||||
|
brew "pwgen"
|
||||||
|
brew "pwntools"
|
||||||
|
brew "python@3.13"
|
||||||
|
brew "qemu"
|
||||||
|
brew "restic"
|
||||||
|
brew "ripgrep"
|
||||||
|
brew "ruby"
|
||||||
|
brew "ruby@3.3"
|
||||||
|
brew "rustup"
|
||||||
|
brew "sass/sass/migrator"
|
||||||
|
brew "sass/sass/sass"
|
||||||
|
brew "shellcheck"
|
||||||
|
brew "smartmontools"
|
||||||
|
brew "starship"
|
||||||
|
brew "tmux"
|
||||||
|
brew "uv"
|
||||||
|
brew "virtualenvwrapper"
|
||||||
|
brew "wget"
|
||||||
|
brew "yt-dlp"
|
||||||
|
brew "zlib"
|
||||||
|
brew "zsh-syntax-highlighting"
|
||||||
|
|
||||||
|
cask "codeql"
|
||||||
|
cask "cyberduck"
|
||||||
|
cask "font-fira-code-nerd-font"
|
||||||
|
cask "font-fira-mono-nerd-font"
|
||||||
|
cask "font-go-mono-nerd-font"
|
||||||
|
cask "font-hack-nerd-font"
|
||||||
|
cask "font-inconsolata-nerd-font"
|
||||||
|
cask "font-symbols-only-nerd-font"
|
||||||
|
cask "font-terminess-ttf-nerd-font"
|
||||||
|
cask "ghidra"
|
||||||
|
cask "gimp"
|
||||||
|
cask "github"
|
||||||
|
cask "iterm2"
|
||||||
|
cask "macfuse"
|
||||||
|
cask "meld"
|
||||||
|
cask "mitmproxy"
|
||||||
|
cask "processmonitor"
|
||||||
|
cask "raycast"
|
||||||
|
cask "rectangle"
|
||||||
|
cask "scroll-reverser"
|
||||||
|
cask "temurin"
|
||||||
|
cask "veracrypt"
|
||||||
|
cask "wezterm"
|
||||||
|
cask "zulu@17"
|
||||||
|
|
||||||
|
def is_corp?
|
||||||
|
# Check for MDM enrollment (Enrolled via DEP: Yes)
|
||||||
|
`profiles status -type enrollment 2>/dev/null`.include?("Enrolled via DEP: Yes")
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_corp?
|
||||||
|
brew "bazelisk", link: false
|
||||||
|
end
|
||||||
|
|
||||||
|
# non-corp
|
||||||
|
if !is_corp?
|
||||||
|
brew "bazelisk"
|
||||||
|
brew "openssh"
|
||||||
|
brew "virt-manager"
|
||||||
|
|
||||||
|
cask "claude-code"
|
||||||
|
cask "cryptomator"
|
||||||
|
cask "keepassxc"
|
||||||
|
cask "gcloud-cli"
|
||||||
|
cask "google-cloud-sdk"
|
||||||
|
cask "keybase"
|
||||||
|
cask "orbstack"
|
||||||
|
cask "jordanbaird-ice"
|
||||||
|
end
|
||||||
75
README.md
75
README.md
@@ -1,39 +1,86 @@
|
|||||||
|
|
||||||
### About ###
|
### About ###
|
||||||
|
|
||||||
This is a repository of configuration files that I like to have on all the
|
This is a repository of configuration files that I like to have on all the
|
||||||
machines that I use. I can just clone the repository and run "repo/setup.sh"
|
machines that I use. For new systems, you can bootstrap by running the
|
||||||
and get most things setup the way I like them.
|
included `clone.sh` script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -L https://raw.githubusercontent.com/Matir/skel/master/clone.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can manually clone the repository and run `./install.sh`.
|
||||||
|
|
||||||
This started just as dotfiles, but expanded to include SSH keys, GPG keys,
|
This started just as dotfiles, but expanded to include SSH keys, GPG keys,
|
||||||
packages I like installed, and an ever-growing setup script. There are various
|
and an ever-growing setup script. There are various
|
||||||
options to install just parts of it, such as on a machine where I only have a
|
options to install just parts of it, such as on a machine where I only have a
|
||||||
user account but no root.
|
user account but no root.
|
||||||
|
|
||||||
This now uses [git-crypt](https://github.com/AGWA/git-crypt) to protect
|
This environment supports using `dotfile_overlays/` or `local_dotfiles/` to
|
||||||
`private_dotfiles` for things I don't want to splash all over the internet. :)
|
manage machine-specific or private configurations. You can use
|
||||||
|
[git-crypt](https://github.com/AGWA/git-crypt) on these overlay directories
|
||||||
|
for things you don't want to splash all over the internet. :)
|
||||||
I still wouldn't check in anything terribly sensitive, like private keys.
|
I still wouldn't check in anything terribly sensitive, like private keys.
|
||||||
|
|
||||||
### Usefulness ###
|
### Usefulness ###
|
||||||
|
|
||||||
Mostly I post this to github so I can quickly grab the things I want, but it
|
Mostly I post this to github so I can quickly grab the things I want, but it
|
||||||
might also be useful to others. Feel free to raise an issue if you have any
|
might also be useful to others. Feel free to raise an issue if you have any
|
||||||
questions. I don't anticipating taking merge requests -- make your own
|
questions. I don't anticipating taking merge requests -- make your own
|
||||||
dotfiles. ;)
|
dotfiles. ;)
|
||||||
|
|
||||||
### Options ###
|
### Options ###
|
||||||
|
|
||||||
|
### macOS-like Copy/Paste ###
|
||||||
|
|
||||||
|
To address keyboard shortcut conflicts between operating systems, this environment
|
||||||
|
now supports using `Alt+C` for copy and `Alt+V` for paste, similar to macOS.
|
||||||
|
This functionality is context-aware: it will automatically use `Ctrl+Shift+C/V`
|
||||||
|
in terminals and `Ctrl+C/V` in all other applications.
|
||||||
|
|
||||||
|
This feature requires the following packages to be installed:
|
||||||
|
|
||||||
|
- `xbindkeys`: To listen for the keyboard shortcuts.
|
||||||
|
- `xdotool`: To send the appropriate keypresses.
|
||||||
|
|
||||||
|
On Debian-based systems (like Ubuntu or Kali), you can install them with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install xbindkeys xdotool
|
||||||
|
```
|
||||||
|
|
||||||
|
After installation, the functionality will be enabled automatically on your
|
||||||
|
next login.
|
||||||
|
|
||||||
|
On macOS, you can install the recommended packages using the included `Brewfile`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew bundle install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Packages ###
|
||||||
|
|
||||||
|
The `packages/` directory contains lists of recommended packages. You can
|
||||||
|
manually install a set (e.g., on a Debian-based system) using:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -v "^#" packages/cli | xargs sudo apt-get install -y
|
||||||
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
BASEDIR: Where the skel framework is installed. Defaults to $HOME/.skel
|
BASEDIR: Where the skel framework is installed. Defaults to $HOME/.skel
|
||||||
MINIMAL: Don't do things that require git clones or installation of anything
|
MINIMAL: Don't do things that require git clones or installation of anything
|
||||||
not included in my .skel. (Defaults to 0, installs everything.)
|
not included in my .skel. (e.g., skips vim-plug, TPM) (Defaults to 0)
|
||||||
INSTALL_KEYS: Install GnuPG and SSH keys. SSH keys are placed in
|
INSTALL_KEYS: Install GnuPG and SSH keys. SSH keys are placed in
|
||||||
authorized_keys. (Defaults to 1, installs keys.)
|
authorized_keys. (Defaults to 1, installs keys.)
|
||||||
TRUST_ALL_KEYS: Allow all keys to be used for SSH login, versus a small subset.
|
TRUST_ALL_KEYS: Allow all keys to be used for SSH login, versus a small subset.
|
||||||
INSTALL_PKGS: Install common packages, if on a Debian-like system.
|
VERBOSE: Enable verbose output during installation. (Defaults to 0)
|
||||||
(Defaults to opposite of $MINIMAL.)
|
SAVE: Save the install options to ${BASEDIR}/.installed-prefs
|
||||||
SAVE: Save the install options to ${BASEDIR}/installed-prefs
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### TODO ###
|
### TODO ###
|
||||||
|
|
||||||
[ ] Re-do the installation of packages.
|
- [X] Re-do the installation of packages.
|
||||||
[ ] Make manual installation of sets easy/possible.
|
- [X] Make manual installation of sets easy/possible.
|
||||||
[ ] Make missing packages not cause a full set failure.
|
- [X] Make missing packages not cause a full set failure.
|
||||||
[X] Allow comments and blank lines.
|
- [X] Allow comments and blank lines. in packages
|
||||||
|
|||||||
70
bin/add_hosts_entry
Normal file
70
bin/add_hosts_entry
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# vim: set ft=python:
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import ipaddress
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import sys.path
|
||||||
|
|
||||||
|
|
||||||
|
def generate_hosts_entry(ip_address_str, hostnames):
|
||||||
|
"""
|
||||||
|
Generates a string formatted for /etc/hosts after validating the IP address.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ip_address_str (str): The IP address string.
|
||||||
|
hostnames (list): A list of hostname strings.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A formatted string for /etc/hosts, or None if validation fails.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Validate the IP address
|
||||||
|
ip = ipaddress.ip_address(ip_address_str)
|
||||||
|
except ValueError:
|
||||||
|
print(f"Error: '{ip_address_str}' is not a valid IP address.", file=sys.stderr)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Join hostnames with spaces
|
||||||
|
hostnames_str = " ".join(hostnames)
|
||||||
|
|
||||||
|
# Return the formatted line
|
||||||
|
return f"{ip} {hostnames_str}"
|
||||||
|
|
||||||
|
|
||||||
|
def append_hosts_entry(entry, hosts_file="/etc/hosts"):
|
||||||
|
when = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||||
|
try:
|
||||||
|
with open(hosts_file, "a") as fp:
|
||||||
|
fp.write(f"# Added by add_hosts_entry {when}\n{entry}\n")
|
||||||
|
return True
|
||||||
|
except PermissionError:
|
||||||
|
if os.geteuid() == 0:
|
||||||
|
print(f"Error: failed opening {hosts_file} for writing.",
|
||||||
|
file=sys.stderr)
|
||||||
|
return False
|
||||||
|
return relaunch_with_sudo()
|
||||||
|
|
||||||
|
|
||||||
|
def relaunch_with_sudo():
|
||||||
|
script_abspath = os.path.abspath(sys.argv[0])
|
||||||
|
os.execvp("sudo", [script_abspath] + sys.argv[1:])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Check for correct number of arguments
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print("Usage: python3 generate_hosts_entry.py <ip_address> <hostname1> [hostname2 ...]", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
ip_to_add = sys.argv[1]
|
||||||
|
hostnames_to_add = sys.argv[2:]
|
||||||
|
|
||||||
|
# Generate the entry
|
||||||
|
entry = generate_hosts_entry(ip_to_add, hostnames_to_add)
|
||||||
|
if not entry:
|
||||||
|
sys.exit(1)
|
||||||
|
if not append_hosts_entry(entry):
|
||||||
|
sys.exit(1)
|
||||||
148
bin/cloudy.sh
Executable file
148
bin/cloudy.sh
Executable file
@@ -0,0 +1,148 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -ueo pipefail
|
||||||
|
shopt -s extglob
|
||||||
|
|
||||||
|
# get libraries
|
||||||
|
. ${HOME}/.local/lib/bash/tui.sh
|
||||||
|
|
||||||
|
COMMANDS=(
|
||||||
|
gctx
|
||||||
|
kctx
|
||||||
|
)
|
||||||
|
|
||||||
|
_make_extglob() {
|
||||||
|
local IFS='|'
|
||||||
|
echo "@($*)"
|
||||||
|
}
|
||||||
|
|
||||||
|
CMD_PATTERN=$(_make_extglob "${COMMANDS[@]}")
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Available Subcommands:"
|
||||||
|
printf " - %-10s\n" "${COMMANDS[@]}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_gctx_set() {
|
||||||
|
gcloud config configurations activate "${1}" </dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
_gctx_choose() {
|
||||||
|
local lines=()
|
||||||
|
local default=''
|
||||||
|
local maxnamelen=0
|
||||||
|
local active name description
|
||||||
|
while IFS=$'\t' read -r active name description ; do
|
||||||
|
(( maxnamelen = ( ${#name} > maxnamelen ) ? ${#name} : maxnamelen ))
|
||||||
|
if [[ "$active" == "True" ]] ; then
|
||||||
|
default="${name}"
|
||||||
|
fi
|
||||||
|
lines+=("${name}" "${description}")
|
||||||
|
done < <(gcloud config configurations list \
|
||||||
|
--format='value(is_active, name, format("{} (as {})", properties.core.project, properties.core.account))')
|
||||||
|
local choice
|
||||||
|
if choice=$(printf "%-${maxnamelen}s %s\n" "${lines[@]}" | select_entry "gcloud config" "$default") ; then
|
||||||
|
_gctx_set "${choice}"
|
||||||
|
else
|
||||||
|
echo "No option selected, leaving unchanged."
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_gctx_new() {
|
||||||
|
local cname="${1:-}"
|
||||||
|
if test -z "${cname}" ; then
|
||||||
|
echo "Usage: gctx new <new name>" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
gcloud config configurations create "${cname}"
|
||||||
|
}
|
||||||
|
|
||||||
|
_gctx_name() {
|
||||||
|
gcloud info --format='value(config.active_config_name)'
|
||||||
|
}
|
||||||
|
|
||||||
|
_gctx_clone() {
|
||||||
|
# save old config
|
||||||
|
local oldconfig=()
|
||||||
|
local line
|
||||||
|
while IFS= read -r line ; do
|
||||||
|
old_config+=("$line")
|
||||||
|
done < <(gcloud config configurations describe "$(_gctx_name)" --format='multi(properties:format="flattened[separator=\" \"]")')
|
||||||
|
|
||||||
|
# create new
|
||||||
|
_gctx_new "${1:-}"
|
||||||
|
|
||||||
|
# set config
|
||||||
|
for line in "${oldconfig[@]}" ; do
|
||||||
|
local keyname="${line%% *}"
|
||||||
|
local keypath="${keyname//\.//}"
|
||||||
|
local value="${line#* }"
|
||||||
|
gcloud config set "${keypath}" "${value}"
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
gctx() {
|
||||||
|
local subcmd="${1:-}"
|
||||||
|
shift || true
|
||||||
|
case "${subcmd}" in
|
||||||
|
clone)
|
||||||
|
_gctx_clone "$@"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
new)
|
||||||
|
_gctx_new "$@"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
show)
|
||||||
|
gcloud config configurations list --filter="is_active=True" \
|
||||||
|
--format='table(name, properties.core.account, properties.core.project, properties.compute.zone:label=COMPUTE_DEFAULT_ZONE, properties.compute.region:label=COMPUTE_DEFAULT_REGION)' \
|
||||||
|
"$@"
|
||||||
|
;;
|
||||||
|
list)
|
||||||
|
gcloud config configurations list "$@"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
activate)
|
||||||
|
_gctx_set "$@"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
""|choose)
|
||||||
|
_gctx_choose
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if _gctx_set "${subcmd}" 2>/dev/null ; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "Usage: gctx [show|list|new|choose|clone|<name>]" >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
kctx() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
INVOKED_AS=$(basename "$0")
|
||||||
|
# shellcheck disable=SC2053
|
||||||
|
if [[ "$INVOKED_AS" == $CMD_PATTERN ]] ; then
|
||||||
|
CMD="${INVOKED_AS}"
|
||||||
|
else
|
||||||
|
CMD="${1:-}"
|
||||||
|
shift || usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# shellcheck disable=SC2254
|
||||||
|
case "${CMD}" in
|
||||||
|
${CMD_PATTERN})
|
||||||
|
"${CMD}" "$@"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
8
bin/darwin-env.sh
Executable file
8
bin/darwin-env.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
env > ${TMPDIR}/env-pre
|
||||||
|
. ${HOME}/.shenv
|
||||||
|
env > ${TMPDIR}/env-post
|
||||||
|
for VAR in $(env | cut -d'=' -f1) ; do
|
||||||
|
/bin/launchctl setenv "${VAR}" "$(eval echo \$${VAR})"
|
||||||
|
done
|
||||||
@@ -12,8 +12,17 @@ fi
|
|||||||
|
|
||||||
trap "test -f ${FILENAME} && rm -f ${FILENAME}" EXIT
|
trap "test -f ${FILENAME} && rm -f ${FILENAME}" EXIT
|
||||||
|
|
||||||
|
IOENGINE="libaio"
|
||||||
|
DIRECT=1
|
||||||
|
if [ "$(uname)" = "Darwin" ]; then
|
||||||
|
IOENGINE="posixaio"
|
||||||
|
# macOS doesn't support O_DIRECT in the same way, but fio's direct=1
|
||||||
|
# handles it via F_NOCACHE if supported.
|
||||||
|
DIRECT=1
|
||||||
|
fi
|
||||||
|
|
||||||
fio --loops=5 --size=${BENCHMARK_SIZE} --filename=${FILENAME} \
|
fio --loops=5 --size=${BENCHMARK_SIZE} --filename=${FILENAME} \
|
||||||
--stonewall --ioengine=libaio --direct=1 \
|
--stonewall --ioengine=${IOENGINE} --direct=${DIRECT} \
|
||||||
--name=Seqread --bs=1m --rw=read \
|
--name=Seqread --bs=1m --rw=read \
|
||||||
--name=Seqwrite --bs=1m --rw=write \
|
--name=Seqwrite --bs=1m --rw=write \
|
||||||
--name=512Kread --bs=512k --rw=randread \
|
--name=512Kread --bs=512k --rw=randread \
|
||||||
|
|||||||
93
bin/fix-broken-symlinks.sh
Executable file
93
bin/fix-broken-symlinks.sh
Executable file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# A script to find and remove broken symbolic links in a directory.
|
||||||
|
#
|
||||||
|
# OPTIONS:
|
||||||
|
# -y: Automatically remove all broken links without confirmation.
|
||||||
|
# -q: Quiet mode. Suppress all non-error output.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# --- Default settings ---
|
||||||
|
FORCE_DELETE=0
|
||||||
|
QUIET=0
|
||||||
|
TARGET_DIR="."
|
||||||
|
|
||||||
|
# --- Helper function for logging ---
|
||||||
|
log() {
|
||||||
|
if [ "${QUIET}" -eq 0 ]; then
|
||||||
|
echo "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Usage function ---
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 [-y] [-q] [TARGET_DIRECTORY]"
|
||||||
|
echo " -y: Yes. Automatically remove broken symlinks without confirmation."
|
||||||
|
echo " -q: Quiet. Suppress all output except for errors."
|
||||||
|
echo " TARGET_DIRECTORY: The directory to scan. Defaults to the current directory."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Parse command-line options ---
|
||||||
|
while getopts "yq" opt; do
|
||||||
|
case ${opt} in
|
||||||
|
y)
|
||||||
|
FORCE_DELETE=1
|
||||||
|
;;
|
||||||
|
q)
|
||||||
|
QUIET=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1)) # Remove the parsed options
|
||||||
|
|
||||||
|
# --- Set target directory ---
|
||||||
|
# Use the first remaining argument as the target directory.
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
TARGET_DIR="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "${TARGET_DIR}" ]; then
|
||||||
|
echo "Error: Directory '${TARGET_DIR}' not found." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Searching for broken symlinks in '${TARGET_DIR}'..."
|
||||||
|
|
||||||
|
# --- Main logic ---
|
||||||
|
# Find broken symlinks and process them.
|
||||||
|
find "${TARGET_DIR}" -type l ! -exec test -e {} \; -print0 | while IFS= read -r -d '' link; do
|
||||||
|
if [ "${FORCE_DELETE}" -eq 1 ]; then
|
||||||
|
# No confirmation needed, just delete.
|
||||||
|
if rm "${link}"; then
|
||||||
|
log "Removed '${link}'."
|
||||||
|
else
|
||||||
|
# Errors should still be reported.
|
||||||
|
echo "Failed to remove '${link}'." >&2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# If in quiet mode but not force mode, we can't prompt, so we skip.
|
||||||
|
if [ "${QUIET}" -eq 1 ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
# Ask the user for confirmation.
|
||||||
|
read -p "Remove broken symlink '${link}'? [y/N] " -n 1 -r
|
||||||
|
echo # Move to a new line after input.
|
||||||
|
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
if rm "${link}"; then
|
||||||
|
log "Removed '${link}'."
|
||||||
|
else
|
||||||
|
echo "Failed to remove '${link}'." >&2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "Skipped '${link}'."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log "Cleanup complete."
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
CHROME=`which google-chrome`
|
|
||||||
|
|
||||||
if [ `id -u` != "0" ] ; then
|
|
||||||
exec $CHROME "$@"
|
|
||||||
fi
|
|
||||||
|
|
||||||
CMD="${CHROME} --user-data-dir=${HOME}/.chrome-data-dir \"$@\""
|
|
||||||
su -c "${CMD}" chromeuser
|
|
||||||
@@ -1,3 +1,25 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
CHROME_BINS="google-chrome-beta google-chrome"
|
||||||
|
if [ "$(uname)" = "Darwin" ]; then
|
||||||
|
CHROME_BINS="${CHROME_BINS} /Applications/Google\ Chrome\ Beta.app/Contents/MacOS/Google\ Chrome\ Beta /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for bin in ${CHROME_BINS} ; do
|
||||||
|
if command -v ${bin} >/dev/null 2>&1 ; then
|
||||||
|
CHROME=$(command -v ${bin})
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -z "${CHROME}" ; then
|
||||||
|
echo "Chrome not found!" >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set alternate HOME to use alternate NSS DB
|
||||||
|
export HOME=${HOME}/.chrome-pentest
|
||||||
|
mkdir -p ${HOME}
|
||||||
|
|
||||||
# Launch chrome for burp
|
# Launch chrome for burp
|
||||||
exec /usr/bin/google-chrome-beta --ignore-certificate-errors --user-data-dir=${HOME}/.config/chrome-pentest --proxy-server=127.0.0.1:8080
|
exec "${CHROME}" --user-data-dir=${HOME}/chrome-pentest --proxy-server=127.0.0.1:8080
|
||||||
|
|||||||
14
bin/google-chrome-noroot
Executable file
14
bin/google-chrome-noroot
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
CHROME=`which google-chrome`
|
||||||
|
|
||||||
|
if [ `id -u` != "0" ] ; then
|
||||||
|
exec "$CHROME" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
args=()
|
||||||
|
for x in "$@"; do
|
||||||
|
args+=("$(printf %q "$x")")
|
||||||
|
done
|
||||||
|
|
||||||
|
su -c "$CHROME --user-data-dir=${HOME}/.chrome-data-dir ${args[*]}" chromeuser
|
||||||
137
bin/install_ansible.sh
Executable file
137
bin/install_ansible.sh
Executable file
@@ -0,0 +1,137 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Installs Ansible, trying user-space methods first before falling back to sudo.
|
||||||
|
# This script is designed to be idempotent and safe to run multiple times.
|
||||||
|
|
||||||
|
set -e # Exit immediately if a command exits with a non-zero status.
|
||||||
|
|
||||||
|
# --- Helper Functions ---
|
||||||
|
info() { echo "[INFO] $1"; }
|
||||||
|
warn() { echo "[WARN] $1"; }
|
||||||
|
error() { echo "[ERROR] $1" >&2; exit 1; }
|
||||||
|
|
||||||
|
# --- Main Logic ---
|
||||||
|
|
||||||
|
# 1. Check if Ansible is already installed
|
||||||
|
if command -v ansible >/dev/null 2>&1; then
|
||||||
|
info "Ansible is already installed at $(command -v ansible)."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
info "Ansible not found. Attempting installation..."
|
||||||
|
|
||||||
|
# 2. Try user-space installation (no sudo)
|
||||||
|
info "--- Attempting user-space installation (no sudo required) ---"
|
||||||
|
|
||||||
|
# Try pipx first, as it's the cleanest user-space method
|
||||||
|
if command -v pipx >/dev/null 2>&1; then
|
||||||
|
info "Found pipx. Trying to install Ansible with it..."
|
||||||
|
if pipx install ansible;
|
||||||
|
then
|
||||||
|
# pipx requires adding ~/.local/bin to PATH, which might not be sourced yet.
|
||||||
|
# Check the executable directly.
|
||||||
|
if [[ -x "${HOME}/.local/bin/ansible" ]]; then
|
||||||
|
info "Ansible installed successfully with pipx."
|
||||||
|
info "Please ensure '${HOME}/.local/bin' is in your PATH."
|
||||||
|
info "You may need to restart your shell or run: export PATH=\"$HOME/.local/bin:$PATH\""
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
warn "pipx install seemed to succeed, but ansible executable not found where expected."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "pipx install ansible failed."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try Python's venv module if pipx failed or wasn't present
|
||||||
|
VENV_PATH="${HOME}/.local/share/ansible_venv"
|
||||||
|
# Create a temp path to avoid clobbering a failed install
|
||||||
|
VENV_TEST_PATH="/tmp/ansible_venv_test_$$"
|
||||||
|
if python3 -m venv "${VENV_TEST_PATH}" >/dev/null 2>&1; then
|
||||||
|
rm -rf "${VENV_TEST_PATH}" # Clean up test
|
||||||
|
info "Python's venv module is available. Creating a virtual environment at ${VENV_PATH}..."
|
||||||
|
python3 -m venv "${VENV_PATH}"
|
||||||
|
if "${VENV_PATH}/bin/pip" install --quiet ansible;
|
||||||
|
then
|
||||||
|
info "Ansible installed successfully into a virtual environment."
|
||||||
|
info "To use it, run: '${VENV_PATH}/bin/ansible'"
|
||||||
|
info "To make it available everywhere, add its bin directory to your PATH:"
|
||||||
|
info " echo 'export PATH="${VENV_PATH}/bin:$PATH"' >> ~/.profile"
|
||||||
|
info "(You may need to source ~/.profile or restart your shell)."
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
warn "Failed to install ansible into the virtual environment."
|
||||||
|
rm -rf "${VENV_PATH}" # Clean up failed attempt
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
info "Python's venv module not available or failed to create a test environment."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Fallback to sudo installation
|
||||||
|
info "--- User-space installation failed. Falling back to system-wide installation (sudo required) ---"
|
||||||
|
|
||||||
|
if ! command -v sudo >/dev/null 2>&1; then
|
||||||
|
error "sudo command not found. Cannot attempt system-wide installation. Aborting."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prompt for sudo password upfront so it doesn't happen in the middle of the script
|
||||||
|
info "Sudo privileges are required. You may be prompted for your password."
|
||||||
|
if ! sudo -v; then
|
||||||
|
error "Failed to acquire sudo privileges. Aborting."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect package manager and install
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
info "Detected Debian-based system (apt)."
|
||||||
|
sudo apt-get update -y
|
||||||
|
info "Attempting to install 'ansible' package..."
|
||||||
|
if sudo apt-get install -y ansible;
|
||||||
|
then
|
||||||
|
info "System package 'ansible' installed successfully."
|
||||||
|
else
|
||||||
|
warn "Failed to install 'ansible' package directly. Trying to install prerequisites for user-space install..."
|
||||||
|
if sudo apt-get install -y pipx;
|
||||||
|
then
|
||||||
|
info "Installed pipx. Attempting to install Ansible with it..."
|
||||||
|
if pipx install ansible;
|
||||||
|
then
|
||||||
|
info "Ansible installed successfully with pipx."
|
||||||
|
info "Please ensure '${HOME}/.local/bin' is in your PATH."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
error "Failed to install 'ansible' or 'pipx' via apt. Aborting."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif command -v dnf >/dev/null 2>&1; then
|
||||||
|
info "Detected Red Hat-based system (dnf)."
|
||||||
|
info "Attempting to install 'ansible-core' package..."
|
||||||
|
if ! sudo dnf install -y ansible-core;
|
||||||
|
then
|
||||||
|
error "Failed to install ansible-core via dnf. Aborting."
|
||||||
|
fi
|
||||||
|
elif command -v pacman >/dev/null 2>&1; then
|
||||||
|
info "Detected Arch-based system (pacman)."
|
||||||
|
info "Attempting to install 'ansible' package..."
|
||||||
|
if ! sudo pacman -Syu --noconfirm ansible;
|
||||||
|
then
|
||||||
|
error "Failed to install ansible via pacman. Aborting."
|
||||||
|
fi
|
||||||
|
elif command -v brew >/dev/null 2>&1; then
|
||||||
|
info "Detected macOS (brew)."
|
||||||
|
info "Attempting to install 'ansible' package..."
|
||||||
|
if ! brew install ansible;
|
||||||
|
then
|
||||||
|
error "Failed to install ansible via brew. Aborting."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
error "Could not detect a known package manager (apt, dnf, pacman, brew). Aborting."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Final verification
|
||||||
|
info "--- Verifying final installation ---"
|
||||||
|
if command -v ansible >/dev/null 2>&1; then
|
||||||
|
info "Ansible successfully installed at $(command -v ansible)."
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
error "Installation attempted but the 'ansible' command is still not available. Please check the output for errors."
|
||||||
|
fi
|
||||||
92
bin/install_package.sh
Normal file
92
bin/install_package.sh
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Attempt to install packages regardless of OS
|
||||||
|
|
||||||
|
set -ue
|
||||||
|
|
||||||
|
is_sourced() {
|
||||||
|
if [ -n "${ZSH_VERSION:-}" ]; then
|
||||||
|
case $ZSH_EVAL_CONTEXT in *:file:*) return 0;; esac
|
||||||
|
else # Add additional POSIX-compatible shell names here, if needed.
|
||||||
|
case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh) return 0;; esac
|
||||||
|
fi
|
||||||
|
return 1 # NOT sourced.
|
||||||
|
}
|
||||||
|
|
||||||
|
# Format is <apt name>:<manager>:<alternate name>
|
||||||
|
# Use "-" for alternate name if not available
|
||||||
|
PACKAGE_ALIASES=$(cat <<'EOF'
|
||||||
|
binfmt-support:brew:-
|
||||||
|
cryptsetup:brew:-
|
||||||
|
lvm2:brew:-
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
package_alias() {
|
||||||
|
local manager="$1"
|
||||||
|
local package="$2"
|
||||||
|
local alias=$(echo "$PACKAGE_ALIASES" | \
|
||||||
|
awk -F: -v manager="${manager}" -v package="${package}" \
|
||||||
|
'$1 == package && $2 == manager { print $3 }' 2>/dev/null)
|
||||||
|
echo "${alias:-${package}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_package() {
|
||||||
|
local package="$1"
|
||||||
|
|
||||||
|
# Check for apt-get
|
||||||
|
if command -v apt-get &> /dev/null; then
|
||||||
|
package=$(package_alias apt "${package}")
|
||||||
|
if [ "$package" == "-" ] ; then
|
||||||
|
echo "Package not available on this platform"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "Installing '$package' using apt-get..."
|
||||||
|
sudo apt-get install -y -- "$package"
|
||||||
|
return 0
|
||||||
|
elif command -v yum &> /dev/null; then
|
||||||
|
package=$(package_alias yum "${package}")
|
||||||
|
if [ "$package" == "-" ] ; then
|
||||||
|
echo "Package not available on this platform"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "Installing '$package' using yum..."
|
||||||
|
sudo yum install -y -- "$package"
|
||||||
|
return 0
|
||||||
|
elif command -v pacman &> /dev/null; then
|
||||||
|
package=$(package_alias pacman "${package}")
|
||||||
|
if [ "$package" == "-" ] ; then
|
||||||
|
echo "Package not available on this platform"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "Installing '$package' using pacman..."
|
||||||
|
sudo pacman -S -- "$package"
|
||||||
|
return 0
|
||||||
|
# For macOS, assume Homebrew is installed
|
||||||
|
elif command -v brew &> /dev/null; then
|
||||||
|
package=$(package_alias brew "${package}")
|
||||||
|
if [ "$package" == "-" ] ; then
|
||||||
|
echo "Package not available on this platform"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "Installing '$package' using Homebrew..."
|
||||||
|
brew install -- "$package"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Error: No suitable package manager found."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
is_sourced || {
|
||||||
|
# Get the package name from the command line argument
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "Usage: $0 <package_name>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
package_name="$1"
|
||||||
|
|
||||||
|
# Call the install function
|
||||||
|
install_package "$package_name"
|
||||||
|
}
|
||||||
545
bin/install_tool
545
bin/install_tool
@@ -2,38 +2,57 @@
|
|||||||
|
|
||||||
set -ue
|
set -ue
|
||||||
|
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
trap 'rm -rf -- "${TMPDIR}"' EXIT
|
||||||
|
|
||||||
REINSTALL=0
|
REINSTALL=0
|
||||||
PACKAGES=1
|
PACKAGES=1
|
||||||
|
|
||||||
|
export GO111MODULE=on
|
||||||
|
|
||||||
while getopts -- "-:" a ; do
|
while getopts -- "-:" a ; do
|
||||||
|
# shellcheck disable=SC2154
|
||||||
case "${a}" in
|
case "${a}" in
|
||||||
-)
|
-)
|
||||||
case "${OPTARG}" in
|
case "${OPTARG}" in
|
||||||
reinstall)
|
reinstall)
|
||||||
REINSTALL=1
|
REINSTALL=1
|
||||||
;;
|
;;
|
||||||
no-packages)
|
no-packages)
|
||||||
PACKAGES=0
|
PACKAGES=0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown long option ${OPTARG}" >/dev/stderr
|
echo "Unknown long option ${OPTARG}" >/dev/stderr
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown short option ${OPTARG}" >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
shift $(($OPTIND-1))
|
shift $((OPTIND-1))
|
||||||
|
|
||||||
|
function list_tools {
|
||||||
|
echo "Options:" >/dev/stderr
|
||||||
|
awk 'BEGIN {s=0;FS=")"};/main tool selection/{s=1};/^\s+\w+)$/{if(s==1){print $1}}' "$0" | sort | while read -r opt; do
|
||||||
|
echo -e "\\t${opt}" >/dev/stderr
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
if [ $# -ne 1 ] ; then
|
if [ $# -ne 1 ] ; then
|
||||||
echo "Usage: ${0} <tool>" >/dev/stderr
|
echo "Usage: ${0} <tool>" >/dev/stderr
|
||||||
|
list_tools
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
TOOL=${1}
|
TOOL=${1}
|
||||||
|
|
||||||
function die {
|
function die {
|
||||||
echo $* >/dev/stderr
|
echo "$@" >/dev/stderr
|
||||||
return 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function install_pkgs {
|
function install_pkgs {
|
||||||
@@ -41,23 +60,22 @@ function install_pkgs {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
# TODO: check if packages are already installed
|
# TODO: check if packages are already installed
|
||||||
if [ `id -u` -ne "0" ] ; then
|
if [ "$(id -u)" -ne "0" ] ; then
|
||||||
sudo apt-get -y install $* || (
|
sudo apt-get -y install "$@" || (
|
||||||
echo -n "Unable to install packages, please ensure these " >/dev/stderr
|
echo -n "Unable to install packages, please ensure these " >/dev/stderr
|
||||||
echo "are installed, then run with --no-packages." >/dev/stderr
|
echo "are installed, then run with --no-packages." >/dev/stderr
|
||||||
echo $*
|
echo "$@"
|
||||||
false )
|
false )
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
apt-get -y install $*
|
apt-get -y install "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
function download {
|
function download {
|
||||||
SRC=${1}
|
SRC=${1}
|
||||||
DST=${2}
|
DST=${2}
|
||||||
echo -n "Downloading ${SRC} to ${DST}..." >&2
|
echo -n "Downloading ${SRC} to ${DST}..." >&2
|
||||||
# TODO: consider curl instead?
|
curl -fL -o "${DST}" "${SRC}"
|
||||||
wget --no-server-response -q -O ${DST} --content-disposition ${SRC}
|
|
||||||
echo " done." >&2
|
echo " done." >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,192 +85,479 @@ function check_sudo {
|
|||||||
|
|
||||||
function add_bin_symlink {
|
function add_bin_symlink {
|
||||||
local TARGET NAME BINDIR
|
local TARGET NAME BINDIR
|
||||||
TARGET=${1}
|
TARGET="${1}"
|
||||||
NAME=${2:-$(basename ${1})}
|
NAME="${2:-$(basename "${1}")}"
|
||||||
BINDIR=${HOME}/bin/tools/
|
BINDIR="${HOME}/bin/tools/"
|
||||||
mkdir -p -- ${BINDIR}
|
mkdir -p -- "${BINDIR}"
|
||||||
ln -sf ${DESTDIR}/${TARGET} ${BINDIR}/${NAME}
|
ln -sf "${DESTDIR}/${TARGET}" "${BINDIR}/${NAME}"
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdir -p "${HOME}/tools"
|
mkdir -p "${HOME}/tools"
|
||||||
|
|
||||||
DESTDIR="${HOME}/tools/${TOOL}"
|
DESTDIR="${HOME}/tools/${TOOL}"
|
||||||
|
|
||||||
if [ -d ${DESTDIR} ] ; then
|
function makedest {
|
||||||
if [ ${REINSTALL} -eq 1 ] ; then
|
if [ -d "${DESTDIR}" ] ; then
|
||||||
rm -ri ${DESTDIR}
|
if [ "${REINSTALL}" -eq 1 ] ; then
|
||||||
else
|
rm -ri "${DESTDIR}"
|
||||||
echo "${DESTDIR} exists but not reinstalling." >/dev/stderr
|
else
|
||||||
|
echo "${DESTDIR} exists but not reinstalling." >/dev/stderr
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
mkdir -p "${DESTDIR}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function makedest_or_die {
|
||||||
|
makedest || die "Aborting."
|
||||||
|
}
|
||||||
|
|
||||||
|
function deb_only {
|
||||||
|
# Error if not on a debian or derivative
|
||||||
|
if ! test -f /etc/debian_version ; then
|
||||||
|
echo "This tool only available for debian." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
}
|
||||||
|
|
||||||
|
function get_latest_github_release_url {
|
||||||
|
local repo="$1"
|
||||||
|
local glob="$2"
|
||||||
|
curl -s "https://api.github.com/repos/${repo}/releases/latest" | \
|
||||||
|
jq -r ".assets[] | select(.name|test(\"${glob}\")) | .browser_download_url"
|
||||||
|
}
|
||||||
|
|
||||||
|
function require_pipx {
|
||||||
|
command -v pipx >/dev/null 2>&1 || die "Requires pipx"
|
||||||
|
}
|
||||||
|
|
||||||
# Begin main tool selection
|
# Begin main tool selection
|
||||||
case ${TOOL} in
|
case ${TOOL} in
|
||||||
john)
|
john)
|
||||||
|
deb_only
|
||||||
|
makedest_or_die
|
||||||
install_pkgs libssl-dev git build-essential yasm libgmp-dev libpcap-dev \
|
install_pkgs libssl-dev git build-essential yasm libgmp-dev libpcap-dev \
|
||||||
pkg-config libbz2-dev libopenmpi-dev openmpi-bin libnss3-dev \
|
pkg-config libbz2-dev libopenmpi-dev openmpi-bin libnss3-dev \
|
||||||
libkrb5-dev libgmp-dev
|
libkrb5-dev libgmp-dev
|
||||||
jtemp=`mktemp -d`
|
jtemp="${TMPDIR}/john"
|
||||||
git clone https://github.com/magnumripper/JohnTheRipper.git ${jtemp}/john
|
mkdir -p "${jtemp}"
|
||||||
cd ${jtemp}/john/src
|
git clone https://github.com/magnumripper/JohnTheRipper.git "${jtemp}/john"
|
||||||
|
cd "${jtemp}/john/src" || exit
|
||||||
./configure && make -sj2
|
./configure && make -sj2
|
||||||
mkdir -p ${DESTDIR}
|
cp -r "${jtemp}"/john/run/* "${DESTDIR}"
|
||||||
cp -r ${jtemp}/john/run/* ${DESTDIR}
|
|
||||||
rm -rf ${jtemp}
|
|
||||||
# Persistent files
|
# Persistent files
|
||||||
mkdir -p ${HOME}/.john
|
mkdir -p "${HOME}/.john"
|
||||||
touch ${HOME}/.john/john.pot
|
touch "${HOME}/.john/john.pot"
|
||||||
ln -sf ${HOME}/.john/* ${DESTDIR}
|
ln -sf "${HOME}/.john/*" "${DESTDIR}"
|
||||||
add_bin_symlink john
|
add_bin_symlink john
|
||||||
;;
|
;;
|
||||||
wordlists)
|
wordlists)
|
||||||
mkdir -p ${DESTDIR}
|
makedest
|
||||||
download \
|
download \
|
||||||
http://downloads.skullsecurity.org/passwords/rockyou.txt.bz2 \
|
"http://downloads.skullsecurity.org/passwords/rockyou.txt.bz2" \
|
||||||
${DESTDIR}/rockyou.txt.bz2
|
"${DESTDIR}/rockyou.txt.bz2"
|
||||||
bunzip2 ${DESTDIR}/rockyou.txt.bz2
|
bunzip2 "${DESTDIR}/rockyou.txt.bz2"
|
||||||
download \
|
download \
|
||||||
http://downloads.skullsecurity.org/passwords/phpbb.txt.bz2 \
|
"http://downloads.skullsecurity.org/passwords/phpbb.txt.bz2" \
|
||||||
${DESTDIR}/phpbb.txt.bz2
|
"${DESTDIR}/phpbb.txt.bz2"
|
||||||
bunzip2 ${DESTDIR}/phpbb.txt.bz2
|
bunzip2 "${DESTDIR}/phpbb.txt.bz2"
|
||||||
download \
|
download \
|
||||||
http://downloads.skullsecurity.org/passwords/hak5.txt.bz2 \
|
"http://downloads.skullsecurity.org/passwords/hak5.txt.bz2" \
|
||||||
${DESTDIR}/hak5.txt.bz2
|
"${DESTDIR}/hak5.txt.bz2"
|
||||||
bunzip2 ${DESTDIR}/hak5.txt.bz2
|
bunzip2 "${DESTDIR}/hak5.txt.bz2"
|
||||||
;;
|
;;
|
||||||
seclists)
|
seclists)
|
||||||
git clone https://github.com/danielmiessler/SecLists.git ${DESTDIR}
|
git clone https://github.com/danielmiessler/SecLists.git "${DESTDIR}"
|
||||||
|
;;
|
||||||
|
werdlists)
|
||||||
|
git clone --depth 1 https://github.com/decal/werdlists.git "${DESTDIR}"
|
||||||
;;
|
;;
|
||||||
gcloud)
|
gcloud)
|
||||||
mkdir -p ${DESTDIR}
|
makedest_or_die
|
||||||
gtemp=`mktemp -d`
|
|
||||||
gbase="https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/"
|
gbase="https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/"
|
||||||
# TODO: find a way to make this version independent
|
gsdk=$(curl -s https://cloud.google.com/sdk/docs/install-sdk | grep -o "google-cloud-sdk-[0-9.]*-linux-x86_64.tar.gz" | head -n 1)
|
||||||
gsdk="google-cloud-sdk-209.0.0-linux-x86_64.tar.gz"
|
download "${gbase}${gsdk}" "${TMPDIR}/gcloud.tar.gz"
|
||||||
download "${gbase}${gsdk}" /tmp/gcloud.tar.gz
|
tar zxf "${TMPDIR}/gcloud.tar.gz" --strip-components=1 -C "${DESTDIR}"
|
||||||
tar zxf /tmp/gcloud.tar.gz --strip-components=1 -C ${DESTDIR}
|
|
||||||
rm /tmp/gcloud.tar.gz
|
|
||||||
add_bin_symlink bin/gcloud
|
add_bin_symlink bin/gcloud
|
||||||
;;
|
;;
|
||||||
android-sdk)
|
android-sdk)
|
||||||
# TODO: find a way to make this version independent
|
asdk=$(curl -s https://developer.android.com/studio/releases/platform-tools | grep -o "https://dl.google.com/android/repository/platform-tools_r[0-9.]*-linux.zip" | head -n 1)
|
||||||
asdk="https://dl.google.com/android/repository/tools_r25.2.3-linux.zip"
|
download "${asdk}" "${TMPDIR}/android-tools.zip"
|
||||||
download ${asdk} /tmp/android-tools.zip
|
unzip -d "${DESTDIR}" "${TMPDIR}/android-tools.zip"
|
||||||
unzip -d ${DESTDIR} /tmp/android-tools.zip
|
|
||||||
rm /tmp/android-tools.zip
|
|
||||||
# Install components
|
# Install components
|
||||||
${DESTDIR}/tools/bin/sdkmanager "emulator" "platform-tools"
|
"${DESTDIR}/tools/bin/sdkmanager" "emulator" "platform-tools"
|
||||||
;;
|
;;
|
||||||
burp)
|
burp)
|
||||||
# Install latest burp free
|
# Install latest burp free
|
||||||
mkdir -p ${DESTDIR}
|
makedest
|
||||||
download \
|
if ! download \
|
||||||
https://portswigger.net/DownloadUpdate.ashx\?Product\=Free \
|
https://portswigger.net/DownloadUpdate.ashx\?Product=Free \
|
||||||
${DESTDIR}/burp-free.jar
|
"${DESTDIR}/burp-free.jar" ; then
|
||||||
if [ $? -ne 0 ] ; then
|
|
||||||
echo "Download failed." >&2
|
echo "Download failed." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ -x /usr/bin/jarwrapper ] ; then
|
if [ -x /usr/bin/jarwrapper ] ; then
|
||||||
# We have binfmt support for jar, so add to bin
|
# We have binfmt support for jar, so add to bin
|
||||||
chmod +x ${DESTDIR}/*.jar
|
chmod +x "${DESTDIR}"/*.jar
|
||||||
ln -sf ${DESTDIR}/*.jar ${HOME}/bin/burp
|
ln -sf "${DESTDIR}"/*.jar "${HOME}/bin/burp"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
mitmproxy)
|
mitmproxy)
|
||||||
ver=$(python -c 'import urllib2; import json; print(json.load(urllib2.urlopen("https://api.github.com/repos/mitmproxy/mitmproxy/releases/latest"))["name"].replace("v",""))')
|
makedest_or_die
|
||||||
download https://snapshots.mitmproxy.org/${ver}/mitmproxy-${ver}-linux.tar.gz /tmp/mitmproxy.tar.gz
|
download \
|
||||||
mkdir -p ${DESTDIR}
|
"$(get_latest_github_release_url "mitmproxy/mitmproxy" ".*-linux\\.tar\\.gz")" \
|
||||||
tar zx -C ${DESTDIR} -f /tmp/mitmproxy.tar.gz
|
"${TMPDIR}/mitmproxy.tar.gz"
|
||||||
rm /tmp/mitmproxy.tar.gz
|
tar zx -C "${DESTDIR}" -f "${TMPDIR}/mitmproxy.tar.gz"
|
||||||
add_bin_symlink mitmproxy
|
add_bin_symlink mitmproxy
|
||||||
add_bin_symlink mitmweb
|
add_bin_symlink mitmweb
|
||||||
|
add_bin_symlink mitmdump
|
||||||
;;
|
;;
|
||||||
esp)
|
esp)
|
||||||
|
makedest_or_die
|
||||||
src="https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz"
|
src="https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz"
|
||||||
download ${src} /tmp/esp32.tar.gz
|
download "${src}" "${TMPDIR}/esp32.tar.gz"
|
||||||
mkdir -p ${DESTDIR}
|
tar zx -C "${DESTDIR}" -f "${TMPDIR}/esp32.tar.gz"
|
||||||
tar zx -C ${DESTDIR} -f /tmp/esp32.tar.gz
|
git clone --recursive https://github.com/espressif/esp-idf.git "${DESTDIR}/esp-idf"
|
||||||
rm /tmp/esp32.tar.gz
|
|
||||||
git clone --recursive https://github.com/espressif/esp-idf.git ${DESTDIR}/esp-idf
|
|
||||||
;;
|
;;
|
||||||
dex2jar)
|
dex2jar)
|
||||||
src="https://github.com/pxb1988/dex2jar/releases/download/2.0/dex-tools-2.0.zip"
|
makedest_or_die
|
||||||
download ${src} /tmp/dex2jar.zip
|
src="https://github.com/pxb1988/dex2jar/releases/download/v2.4/dex-tools-v2.4.zip"
|
||||||
tmpd=`mktemp -d`
|
download "${src}" "${TMPDIR}/dex2jar.zip"
|
||||||
unzip -d ${tmpd} /tmp/dex2jar.zip
|
tmpd="${TMPDIR}/dex2jar"
|
||||||
mv ${tmpd}/* ${DESTDIR}
|
mkdir -p "${tmpd}"
|
||||||
rm /tmp/dex2jar.zip
|
unzip -d "${tmpd}" "${TMPDIR}/dex2jar.zip"
|
||||||
rm -rf ${tmpd}
|
mv "${tmpd}"/dex-tools-*/* "${DESTDIR}"
|
||||||
rm ${DESTDIR}/*.bat
|
rm "${DESTDIR}"/*.bat
|
||||||
chmod +x ${DESTDIR}/*.sh
|
chmod +x "${DESTDIR}"/*.sh
|
||||||
;;
|
;;
|
||||||
proxmark3)
|
proxmark3)
|
||||||
|
deb_only
|
||||||
install_pkgs p7zip git build-essential libreadline5 libreadline-dev \
|
install_pkgs p7zip git build-essential libreadline5 libreadline-dev \
|
||||||
libusb-0.1-4 libusb-dev libqt4-dev perl pkg-config wget libncurses5-dev \
|
libusb-0.1-4 libusb-dev libqt4-dev perl pkg-config wget libncurses5-dev \
|
||||||
gcc-arm-none-eabi libstdc++-arm-none-eabi-newlib
|
gcc-arm-none-eabi libstdc++-arm-none-eabi-newlib
|
||||||
src="https://github.com/Proxmark/proxmark3.git"
|
src="https://github.com/Proxmark/proxmark3.git"
|
||||||
git clone ${src} ${DESTDIR}
|
git clone "${src}" "${DESTDIR}"
|
||||||
cd ${DESTDIR}
|
cd "${DESTDIR}" || exit
|
||||||
make -sj2
|
make -sj2
|
||||||
check_sudo && sudo /bin/sh -c \
|
check_sudo && sudo /bin/sh -c \
|
||||||
"cp -rf driver/78-mm-usb-device-blacklist.rules \
|
"cp -rf driver/78-mm-usb-device-blacklist.rules \
|
||||||
/etc/udev/rules.d/77-mm-usb-device-blacklist.rules &&
|
/etc/udev/rules.d/77-mm-usb-device-blacklist.rules &&\
|
||||||
udevadm control --reload-rules"
|
udevadm control --reload-rules"
|
||||||
;;
|
;;
|
||||||
|
pm3iceman)
|
||||||
|
deb_only
|
||||||
|
# arch:
|
||||||
|
# sudo pacman -Syu git base-devel readline bzip2 lz4 arm-none-eabi-gcc arm-none-eabi-newlib qt5-base bluez python gd --needed
|
||||||
|
install_pkgs git ca-certificates build-essential pkg-config \
|
||||||
|
libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev \
|
||||||
|
libbz2-dev libbluetooth-dev libpython3-dev libssl-dev
|
||||||
|
src="https://github.com/RfidResearchGroup/proxmark3.git"
|
||||||
|
git clone "${src}" "${DESTDIR}"
|
||||||
|
cd "${DESTDIR}" || exit
|
||||||
|
make clean && make -sj2
|
||||||
|
check_sudo && sudo /bin/sh -c \
|
||||||
|
"cp -rf ./driver/77-pm3-usb-device-blacklist.rules \
|
||||||
|
/etc/udev/rules.d/77-pm3-usb-device-blacklist.rules &&\
|
||||||
|
udevadm control --reload-rules"
|
||||||
|
add_bin_symlink pm3
|
||||||
|
;;
|
||||||
cyberchef)
|
cyberchef)
|
||||||
mkdir -p ${DESTDIR}
|
makedest
|
||||||
src=$(python -c 'import urllib2; import json; print(filter(lambda x: x["name"]=="cyberchef.htm", json.load(urllib2.urlopen("https://api.github.com/repos/gchq/CyberChef/releases/latest"))["assets"])[0]["browser_download_url"])')
|
cd "${DESTDIR}" || exit
|
||||||
download ${src} ${DESTDIR}/cyberchef.html
|
download \
|
||||||
|
"$(get_latest_github_release_url "gchq/CyberChef" ".*\\.zip")" \
|
||||||
|
"${DESTDIR}/cyberchef.zip"
|
||||||
|
unzip -d "${DESTDIR}" "${DESTDIR}/cyberchef.zip"
|
||||||
|
ln -sf CyberChef*.html "${DESTDIR}/cyberchef.html"
|
||||||
;;
|
;;
|
||||||
apktool)
|
apktool)
|
||||||
mkdir -p ${DESTDIR}
|
makedest_or_die
|
||||||
download \
|
download \
|
||||||
https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool \
|
https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool \
|
||||||
${DESTDIR}/apktool
|
"${DESTDIR}/apktool"
|
||||||
|
jar_url=$(curl -s https://bitbucket.org/iBotPeaches/apktool/downloads/ | grep -o "/iBotPeaches/apktool/downloads/apktool_[0-9.]*.jar" | head -n 1)
|
||||||
download \
|
download \
|
||||||
https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.3.3.jar \
|
"https://bitbucket.org${jar_url}" \
|
||||||
${DESTDIR}/apktool.jar
|
"${DESTDIR}/apktool.jar"
|
||||||
chmod +x ${DESTDIR}/apktool
|
chmod +x "${DESTDIR}/apktool"
|
||||||
add_bin_symlink apktool
|
add_bin_symlink apktool
|
||||||
;;
|
;;
|
||||||
ptf)
|
ptf)
|
||||||
|
makedest_or_die
|
||||||
src="https://github.com/trustedsec/ptf.git"
|
src="https://github.com/trustedsec/ptf.git"
|
||||||
git clone ${src} ${DESTDIR}
|
git clone "${src}" "${DESTDIR}"
|
||||||
;;
|
;;
|
||||||
pwndbg)
|
pwndbg)
|
||||||
if ! which gdb > /dev/null 2>&1 ; then
|
if ! command -v gdb > /dev/null 2>&1 ; then
|
||||||
echo 'No gdb available!' >/dev/stderr
|
echo 'No gdb available!' >/dev/stderr
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
git clone --depth 1 -b stable https://github.com/pwndbg/pwndbg.git ${DESTDIR}
|
git clone --depth 1 -b stable https://github.com/pwndbg/pwndbg.git "${DESTDIR}"
|
||||||
local PY_PACKAGES=${DESTDIR}/vendor
|
PY_PACKAGES=${DESTDIR}/vendor
|
||||||
mkdir -p ${PY_PACKAGES}
|
mkdir -p "${PY_PACKAGES}"
|
||||||
local PYVER=$(gdb -batch -q --nx -ex 'pi import platform; print(".".join(platform.python_version_tuple()[:2]))')
|
PYVER=$(gdb -batch -q --nx -ex 'pi import platform; print(".".join(platform.python_version_tuple()[:2]))')
|
||||||
local PYTHON=$(gdb -batch -q --nx -ex 'pi import sys; print(sys.executable)')
|
PYTHON=$(gdb -batch -q --nx -ex 'pi import sys; print(sys.executable)')
|
||||||
PYTHON="${PYTHON}${PYVER}"
|
PYTHON="${PYTHON}${PYVER}"
|
||||||
${PYTHON} -m pip install --target ${PY_PACKAGES} -Ur ${DESTDIR}/requirements.txt
|
"${PYTHON}" -m pip install --target "${PY_PACKAGES}" -Ur "${DESTDIR}/requirements.txt"
|
||||||
${PYTHON} -m pip install --target ${PY_PACKAGES} -U capstone unicorn
|
"${PYTHON}" -m pip install --target "${PY_PACKAGES}" -U capstone unicorn
|
||||||
# capstone package is broken
|
# capstone package is broken, find and copy the library manually
|
||||||
cp ${PY_PACKAGES}/usr/lib/*/dist-packages/capstone/libcapstone.so ${PY_PACKAGES}/capstone
|
capstone_so_path=$(find "${PY_PACKAGES}/usr/lib" -name "libcapstone.so" -type f)
|
||||||
|
if [ -z "${capstone_so_path}" ]; then
|
||||||
|
die "Could not find libcapstone.so for pwndbg."
|
||||||
|
elif [ "$(echo "${capstone_so_path}" | wc -l)" -ne 1 ]; then
|
||||||
|
die "Found multiple libcapstone.so files for pwndbg, aborting."
|
||||||
|
fi
|
||||||
|
cp "${capstone_so_path}" "${PY_PACKAGES}/capstone/"
|
||||||
;;
|
;;
|
||||||
gef)
|
gef)
|
||||||
if ! which gdb > /dev/null 2>&1 ; then
|
makedest_or_die
|
||||||
|
if ! command -v gdb > /dev/null 2>&1 ; then
|
||||||
echo 'No gdb available!' >/dev/stderr
|
echo 'No gdb available!' >/dev/stderr
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
mkdir -p ${DESTDIR}
|
|
||||||
download \
|
download \
|
||||||
https://github.com/hugsy/gef/raw/master/gef.py \
|
https://github.com/hugsy/gef/raw/master/gef.py \
|
||||||
${DESTDIR}/gef.py
|
"${DESTDIR}/gef.py"
|
||||||
|
;;
|
||||||
|
aflplusplus)
|
||||||
|
deb_only
|
||||||
|
install_pkgs libtool-bin libglib2.0-dev libpixman-1-dev clang clang-tools \
|
||||||
|
llvm python3-setuptools
|
||||||
|
git clone "https://github.com/vanhauser-thc/AFLplusplus" "${DESTDIR}"
|
||||||
|
make -C "${DESTDIR}" distrib
|
||||||
|
;;
|
||||||
|
exploitdb)
|
||||||
|
if test -d "${DESTDIR}" ; then
|
||||||
|
echo "Already installed, updating instead..." >/dev/stderr
|
||||||
|
"${DESTDIR}/searchsploit" -u
|
||||||
|
else
|
||||||
|
git clone --depth 1 \
|
||||||
|
https://github.com/offensive-security/exploitdb.git \
|
||||||
|
"${DESTDIR}"
|
||||||
|
add_bin_symlink searchsploit
|
||||||
|
cp "${DESTDIR}/.searchsploit_rc" "${HOME}/.searchsploit_rc"
|
||||||
|
sed -i "s|/opt/exploitdb|${DESTDIR}|" "${HOME}/.searchsploit_rc"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
cura)
|
||||||
|
makedest
|
||||||
|
download \
|
||||||
|
"$(get_latest_github_release_url "Ultimaker/Cura" ".*\\.AppImage")" \
|
||||||
|
"${DESTDIR}/Cura.AppImage"
|
||||||
|
chmod +x "${DESTDIR}/Cura.AppImage"
|
||||||
|
add_bin_symlink "Cura.AppImage" cura
|
||||||
|
;;
|
||||||
|
rr)
|
||||||
|
deb_only
|
||||||
|
check_sudo
|
||||||
|
download \
|
||||||
|
"$(get_latest_github_release_url "mozilla/rr" ".*-Linux-.*\\.deb")" \
|
||||||
|
"${TMPDIR}/rr.deb"
|
||||||
|
sudo dpkg -i "${TMPDIR}/rr.deb"
|
||||||
|
;;
|
||||||
|
nmap-parse-output)
|
||||||
|
git clone --depth 1 \
|
||||||
|
https://github.com/ernw/nmap-parse-output.git \
|
||||||
|
"${DESTDIR}"
|
||||||
|
add_bin_symlink nmap-parse-output
|
||||||
|
cat <<EOF >"${HOME}/.zshrc.d/99-nmap-parse-output.zsh"
|
||||||
|
if test -d ${DESTDIR} ; then
|
||||||
|
autoload bashcompinit
|
||||||
|
bashcompinit
|
||||||
|
source ${DESTDIR}/_nmap-parse-output
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
logiops)
|
||||||
|
deb_only
|
||||||
|
install_pkgs cmake libevdev-dev libudev-dev libconfig++-dev checkinstall
|
||||||
|
git clone "https://github.com/PixlOne/logiops.git" "${DESTDIR}"
|
||||||
|
mkdir -p "${DESTDIR}/build"
|
||||||
|
cd "${DESTDIR}/build" || exit
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
check_sudo
|
||||||
|
sudo checkinstall --pkgname logiops --maintainer "${USER}" -y
|
||||||
|
;;
|
||||||
|
aws)
|
||||||
|
DN="${TMPDIR}/aws"
|
||||||
|
mkdir -p "${DN}"
|
||||||
|
cd "${DN}" || exit
|
||||||
|
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "${DN}/awscliv2.zip"
|
||||||
|
unzip "${DN}/awscliv2.zip"
|
||||||
|
mv "${DN}/aws/dist" "${DESTDIR}"
|
||||||
|
add_bin_symlink aws
|
||||||
|
;;
|
||||||
|
tmpmail)
|
||||||
|
install_pkgs curl w3m jq
|
||||||
|
mkdir -p "${DESTDIR}"
|
||||||
|
download "https://git.io/tmpmail" "${DESTDIR}/tmpmail"
|
||||||
|
chmod +x "${DESTDIR}/tmpmail"
|
||||||
|
add_bin_symlink tmpmail
|
||||||
|
;;
|
||||||
|
gf)
|
||||||
|
install_pkgs golang-go silversearcher-ag
|
||||||
|
go install github.com/tomnomnom/gf@latest
|
||||||
|
mkdir -p "${HOME}/.config"
|
||||||
|
if test -d "${HOME}/.config/gf" ; then
|
||||||
|
git -C "${HOME}/.config/gf" pull
|
||||||
|
else
|
||||||
|
git clone https://github.com/Matir/gf-patterns.git "${HOME}/.config/gf"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
gron)
|
||||||
|
go install github.com/tomnomnom/gron@latest
|
||||||
|
;;
|
||||||
|
httprobe)
|
||||||
|
go install github.com/tomnomnom/httprobe@latest
|
||||||
|
;;
|
||||||
|
ffuf)
|
||||||
|
go install github.com/ffuf/ffuf@latest
|
||||||
|
;;
|
||||||
|
gobuster)
|
||||||
|
go install github.com/OJ/gobuster@latest
|
||||||
|
;;
|
||||||
|
amass)
|
||||||
|
go install github.com/OWASP/Amass/v3/...
|
||||||
|
;;
|
||||||
|
cht.sh)
|
||||||
|
install_pkgs rlwrap
|
||||||
|
mkdir -p "${DESTDIR}"
|
||||||
|
download "https://cht.sh/:cht.sh" "${DESTDIR}/cht.sh"
|
||||||
|
chmod +x "${DESTDIR}/cht.sh"
|
||||||
|
add_bin_symlink cht.sh
|
||||||
|
;;
|
||||||
|
age)
|
||||||
|
go install filippo.io/age/cmd/age@latest
|
||||||
|
go install filippo.io/age/cmd/age-keygen@latest
|
||||||
|
;;
|
||||||
|
docker-compose)
|
||||||
|
mkdir -p "${DESTDIR}"
|
||||||
|
latest_version=$(curl -s "https://api.github.com/repos/docker/compose/releases/latest" | jq -r '.tag_name')
|
||||||
|
curl -L \
|
||||||
|
"https://github.com/docker/compose/releases/download/${latest_version}/docker-compose-$(uname -s)-$(uname -m)" \
|
||||||
|
-o "${DESTDIR}/docker-compose"
|
||||||
|
chmod +x "${DESTDIR}/docker-compose"
|
||||||
|
add_bin_symlink docker-compose
|
||||||
|
;;
|
||||||
|
tldr)
|
||||||
|
require_pipx
|
||||||
|
pipx install tldr
|
||||||
|
;;
|
||||||
|
blint)
|
||||||
|
require_pipx
|
||||||
|
pipx install blint
|
||||||
|
;;
|
||||||
|
dust)
|
||||||
|
if ! command -v cargo >/dev/null 2>&1 ; then
|
||||||
|
echo "This needs cargo (for rust)!" >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cargo install du-dust
|
||||||
|
;;
|
||||||
|
bottom)
|
||||||
|
if ! command -v cargo >/dev/null 2>&1 ; then
|
||||||
|
echo "This needs cargo (for rust)!" >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cargo install bottom
|
||||||
|
;;
|
||||||
|
delta)
|
||||||
|
deb_only
|
||||||
|
if ! check_sudo ; then
|
||||||
|
echo "Must be able to run as sudo."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
dpkg_url=$(get_latest_github_release_url "dandavison/delta" ".*_amd64.deb")
|
||||||
|
dpkg_name="${TMPDIR}/delta_amd64.deb"
|
||||||
|
download "${dpkg_url}" "${dpkg_name}"
|
||||||
|
sudo dpkg -i "${dpkg_name}"
|
||||||
|
;;
|
||||||
|
ropper)
|
||||||
|
deb_only
|
||||||
|
install_pkgs python3-z3
|
||||||
|
pip3 install --user pyvex ropper
|
||||||
|
;;
|
||||||
|
kubeconform)
|
||||||
|
go install github.com/yannh/kubeconform/cmd/kubeconform@latest
|
||||||
|
;;
|
||||||
|
kubectx)
|
||||||
|
git clone https://github.com/ahmetb/kubectx.git "${DESTDIR}"
|
||||||
|
add_bin_symlink kubectx
|
||||||
|
add_bin_symlink kubens
|
||||||
|
COMPDIR="${HOME}/.zshrc.completions"
|
||||||
|
mkdir -p "${COMPDIR}"
|
||||||
|
ln -sf "${DESTDIR}/completion/_kubectx.zsh" "${COMPDIR}"
|
||||||
|
ln -sf "${DESTDIR}/completion/_kubens.zsh" "${COMPDIR}"
|
||||||
|
;;
|
||||||
|
starship)
|
||||||
|
mkdir -p "${DESTDIR}"
|
||||||
|
download \
|
||||||
|
"https://github.com/starship/starship/releases/latest/download/starship-$(uname -m)-unknown-linux-musl.tar.gz" \
|
||||||
|
"${TMPDIR}/starship.tar.gz"
|
||||||
|
tar -C "${DESTDIR}" -zxf "${TMPDIR}/starship.tar.gz" starship
|
||||||
|
add_bin_symlink starship
|
||||||
|
;;
|
||||||
|
arduino-cli)
|
||||||
|
mkdir -p "${DESTDIR}"
|
||||||
|
download \
|
||||||
|
"https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Linux_64bit.tar.gz" \
|
||||||
|
"${TMPDIR}/arduino-cli.tar.gz"
|
||||||
|
tar -C "${DESTDIR}" -zxf "${TMPDIR}/arduino-cli.tar.gz" arduino-cli
|
||||||
|
add_bin_symlink arduino-cli
|
||||||
|
;;
|
||||||
|
ghidra)
|
||||||
|
zip_url=$(get_latest_github_release_url "NationalSecurityAgency/ghidra" ".*\\.zip")
|
||||||
|
download "${zip_url}" "${TMPDIR}/ghidra.zip"
|
||||||
|
unzip -d "${DESTDIR}" "${TMPDIR}/ghidra.zip"
|
||||||
|
mv "${DESTDIR}"/*/* "${DESTDIR}"
|
||||||
|
add_bin_symlink ghidraRun ghidra
|
||||||
|
;;
|
||||||
|
doctl)
|
||||||
|
# TODO: other architectures
|
||||||
|
tar_url=$(get_latest_github_release_url "digitalocean/doctl" ".*linux-amd64\\.tar\\.gz")
|
||||||
|
download "${tar_url}" "${TMPDIR}/doctl.tar.gz"
|
||||||
|
mkdir -p "${DESTDIR}"
|
||||||
|
tar -C "${DESTDIR}" -zxf "${TMPDIR}/doctl.tar.gz" "doctl"
|
||||||
|
add_bin_symlink doctl
|
||||||
|
;;
|
||||||
|
rustup)
|
||||||
|
rustup_init="${TMPDIR}/rustup-init.sh"
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o "${rustup_init}"
|
||||||
|
sh "${rustup_init}" --no-modify-path -y
|
||||||
|
;;
|
||||||
|
igrep)
|
||||||
|
if ! command -v cargo >/dev/null 2>&1 ; then
|
||||||
|
echo "This needs cargo (for rust)!" >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cargo install igrep
|
||||||
|
;;
|
||||||
|
unblob)
|
||||||
|
require_pipx
|
||||||
|
pipx install unblob
|
||||||
|
;;
|
||||||
|
fq)
|
||||||
|
go install github.com/wader/fq@latest
|
||||||
|
;;
|
||||||
|
mise)
|
||||||
|
if command -v brew >/dev/null 2>&1; then
|
||||||
|
brew install mise
|
||||||
|
else
|
||||||
|
curl -sSL https://mise.jdx.dev/gpg-key.pub | gpg --import
|
||||||
|
INSTALL_FILE_AND_SIG="${TMPDIR}/install.sh.sig"
|
||||||
|
download "https://mise.jdx.dev/install.sh.sig" "${INSTALL_FILE_AND_SIG}"
|
||||||
|
DECRYPTED_SCRIPT="${TMPDIR}/mise_install.sh"
|
||||||
|
if gpg --assert-signer 24853EC9F655CE80B48E6C3A8B81C9D17413A06D --decrypt -o "${DECRYPTED_SCRIPT}" "${INSTALL_FILE_AND_SIG}" >/dev/null 2>&1; then
|
||||||
|
sh "${DECRYPTED_SCRIPT}"
|
||||||
|
else
|
||||||
|
die "gpg verification or decryption failed for mise installer"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown tool: ${TOOL}" >/dev/stderr
|
echo "Unknown tool: ${TOOL}" >/dev/stderr
|
||||||
echo "Options:" >/dev/stderr
|
list_tools
|
||||||
for opt in $(awk 'BEGIN {s=0;FS=")"};/main tool selection/{s=1};/^\s+\w+)$/{if(s==1){print $1}}' $0|sort); do
|
|
||||||
echo -e "\t${opt}" >/dev/stderr
|
|
||||||
done
|
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
set -o nounset
|
set -o nounset
|
||||||
set -o errexit
|
set -o errexit
|
||||||
|
|
||||||
|
if [ "$(uname)" != "Linux" ]; then
|
||||||
|
echo "Error: This backup script is only intended for use on Linux." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
DEFAULT=`echo /media/${USER}/[bB]ackup/${USER}/`
|
DEFAULT=`echo /media/${USER}/[bB]ackup/${USER}/`
|
||||||
DEST="${1:-${DEFAULT}}"
|
DEST="${1:-${DEFAULT}}"
|
||||||
|
|
||||||
@@ -26,5 +31,8 @@ function verify_dest {
|
|||||||
|
|
||||||
verify_dest "$DEST"
|
verify_dest "$DEST"
|
||||||
|
|
||||||
exec nice rsync -Hax --delete --exclude-from="$HOME/.rsync_ignore" \
|
time nice rsync -Hax --delete --exclude-from="$HOME/.rsync_ignore" \
|
||||||
--delete-excluded "${HOME}/" "$DEST"
|
--delete-excluded "${HOME}/" "$DEST"
|
||||||
|
echo "Backup completed..."
|
||||||
|
time sync
|
||||||
|
echo "Run finished, safe to unmount."
|
||||||
@@ -835,14 +835,14 @@ case "$1" in
|
|||||||
fi
|
fi
|
||||||
cd /proc
|
cd /proc
|
||||||
N=$2
|
N=$2
|
||||||
if [ -d $N ] ; then
|
if [ -d "$N" ] ; then
|
||||||
# read permissions?
|
# read permissions?
|
||||||
if [ ! -r $N/exe ] ; then
|
if [ ! -r "$N/exe" ] ; then
|
||||||
if !(root_privs) ; then
|
if !(root_privs) ; then
|
||||||
printf "\033[31mNo read permissions for '/proc/$N/exe' (run as root).\033[m\n\n"
|
printf "\033[31mNo read permissions for '/proc/%s/exe' (run as root).\033[m\n\n" "$N"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ ! `readlink $N/exe` ] ; then
|
if [ ! "$(readlink "$N/exe")" ] ; then
|
||||||
printf "\033[31mPermission denied. Requested process ID belongs to a kernel thread.\033[m\n\n"
|
printf "\033[31mPermission denied. Requested process ID belongs to a kernel thread.\033[m\n\n"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -860,9 +860,9 @@ case "$1" in
|
|||||||
printf "\033[31mError: libc not found.\033[m\n\n"
|
printf "\033[31mError: libc not found.\033[m\n\n"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
printf "* Process name (PID) : %s (%d)\n" `head -1 $N/status | cut -b 7-` $N
|
printf "* Process name (PID) : %s (%d)\n" "$(head -1 "$N/status" | cut -b 7-)" "$N"
|
||||||
FS_chk_func_libc=( $(readelf -s $FS_libc | grep _chk@@ | awk '{ print $8 }' | cut -c 3- | sed -e 's/_chk@.*//') )
|
FS_chk_func_libc=( $(readelf -s "$FS_libc" | grep _chk@@ | awk '{ print $8 }' | cut -c 3- | sed -e 's/_chk@.*//') )
|
||||||
FS_functions=( $(readelf -s $2/exe | awk '{ print $8 }' | sed 's/_*//' | sed -e 's/@.*//') )
|
FS_functions=( $(readelf -s "$2/exe" | awk '{ print $8 }' | sed 's/_*//' | sed -e 's/@.*//') )
|
||||||
|
|
||||||
FS_libc_check
|
FS_libc_check
|
||||||
FS_binary_check
|
FS_binary_check
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
LOCKTIME="${SCREENSAVER_MIN:-5}"
|
||||||
LOCKER="i3lock -c 000000"
|
LOCKER="i3lock -c 000000"
|
||||||
# intentionally want word splitting below
|
# intentionally want word splitting below
|
||||||
|
# do not quote this
|
||||||
/usr/bin/xss-lock -- ${LOCKER} &
|
/usr/bin/xss-lock -- ${LOCKER} &
|
||||||
exec /usr/bin/xautolock \
|
exec /usr/bin/xautolock \
|
||||||
-time 5 \
|
-time "${LOCKTIME}" \
|
||||||
-detectsleep \
|
-detectsleep \
|
||||||
-locker "${LOCKER}" \
|
-locker "${LOCKER}" \
|
||||||
-notify 30 \
|
-notify 30 \
|
||||||
22
bin/linux/nvidia_hold.sh
Executable file
22
bin/linux/nvidia_hold.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function list_nvidia_installed {
|
||||||
|
dpkg-query -l '*nvidia*' | grep '^[hi]i' | awk '{print $2}'
|
||||||
|
}
|
||||||
|
|
||||||
|
function hold_or_unhold {
|
||||||
|
list_nvidia_installed | xargs apt-mark "${1:-hold}"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
hold|h)
|
||||||
|
hold_or_unhold hold
|
||||||
|
;;
|
||||||
|
unhold|u)
|
||||||
|
hold_or_unhold unhold
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "$0 <hold|unhold>" >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
46
bin/linux/pactl_helper
Executable file
46
bin/linux/pactl_helper
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function get_active_sink {
|
||||||
|
pactl list short sinks | grep RUNNING | awk '{print $2}'
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_active_source {
|
||||||
|
pactl list short sources | grep RUNNING | awk '{print $2}'
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_default_sink {
|
||||||
|
pactl info | grep '^Default Sink:' | awk '{print $NF}'
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_default_source {
|
||||||
|
pactl info | grep '^Default Source:' | awk '{print $NF}'
|
||||||
|
}
|
||||||
|
|
||||||
|
function micmute {
|
||||||
|
MODE=${1:-toggle}
|
||||||
|
pactl set-source-mute $(get_default_source) ${MODE}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mute {
|
||||||
|
MODE=${1:-toggle}
|
||||||
|
pactl set-sink-mute $(get_default_sink) ${MODE}
|
||||||
|
}
|
||||||
|
|
||||||
|
function volume {
|
||||||
|
VOL="${1}"
|
||||||
|
if test -z "${VOL}" ; then
|
||||||
|
echo "Need volume spec!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
pactl set-sink-volume $(get_default_sink) "${VOL}"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
mute|micmute|volume)
|
||||||
|
"$@"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown command!"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
48
bin/linux/qdisc_span.sh
Executable file
48
bin/linux/qdisc_span.sh
Executable file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ue
|
||||||
|
|
||||||
|
ACTION="add"
|
||||||
|
|
||||||
|
if [ "${1}" == "-d" ] ; then
|
||||||
|
ACTION="del"
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
BRIDGE="${1}"
|
||||||
|
DEST="${2}"
|
||||||
|
|
||||||
|
function setup_span {
|
||||||
|
if tc qdisc show dev "${1}" | grep -q 'qdisc ingress ffff' ; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
tc qdisc add dev "${1}" ingress
|
||||||
|
tc filter add dev "${1}" parent ffff: protocol all u32 match u8 0 0 action mirred egress mirror dev "${DEST}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function del_span {
|
||||||
|
tc qdisc del dev "${1}" ingress
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_iface {
|
||||||
|
case "${ACTION}" in
|
||||||
|
add)
|
||||||
|
setup_span "${1}"
|
||||||
|
;;
|
||||||
|
del)
|
||||||
|
del_span "${1}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown action!"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_bridge_ifaces {
|
||||||
|
bridge link | grep "master ${1}" | cut -d: -f2 | cut -d@ -f1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_bridge_ifaces "${BRIDGE}" | while IFS= read -r iface ; do
|
||||||
|
handle_iface "$iface"
|
||||||
|
done
|
||||||
5
bin/linux/remove-wine-associations
Executable file
5
bin/linux/remove-wine-associations
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
rm -f ~/.local/share/applications/wine*.desktop
|
||||||
|
update-desktop-database ~/.local/share/applications
|
||||||
|
rm -f ~/.local/share/mime/packages/x-wine*.xml
|
||||||
|
update-mime-database ~/.local/share/mime
|
||||||
18
bin/linux/setup/spicerandr.sh
Normal file
18
bin/linux/setup/spicerandr.sh
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ue
|
||||||
|
|
||||||
|
cat >/usr/local/bin/x-resize <<"EOF"
|
||||||
|
#!/bin/sh
|
||||||
|
PATH=/usr/bin:/bin:/usr/local/bin
|
||||||
|
desktopuser=$(/bin/ps -ef | /bin/grep -oP '^\w+ (?=.*vdagent( |$))') || exit 0
|
||||||
|
export DISPLAY=:0
|
||||||
|
export XAUTHORITY=$(eval echo "~$desktopuser")/.Xauthority
|
||||||
|
/usr/bin/xrandr --output $(/usr/bin/xrandr | awk '/ connected/{print $1; exit; }') --auto
|
||||||
|
EOF
|
||||||
|
chmod 755 /usr/local/bin/x-resize
|
||||||
|
|
||||||
|
cat >/etc/udev/rules.d/50-resize.rules <<"EOF"
|
||||||
|
ACTION=="change",KERNEL=="card0", SUBSYSTEM=="drm", RUN+="/usr/local/bin/x-resize"
|
||||||
|
EOF
|
||||||
|
chmod 644 /etc/udev/rules.d/50-resize.rules
|
||||||
36
bin/linux/smart-copy-paste
Executable file
36
bin/linux/smart-copy-paste
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# smart-copy-paste
|
||||||
|
#
|
||||||
|
# This script provides context-aware copy and paste operations, mimicking
|
||||||
|
# macOS behavior (Alt+C/V) while correctly handling terminals that require
|
||||||
|
# the Shift key.
|
||||||
|
|
||||||
|
# Exit silently if xdotool is not installed.
|
||||||
|
if ! command -v xdotool > /dev/null; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the class name of the currently focused window.
|
||||||
|
# We need to get the window on focus, to avoid issues with transparent terminals.
|
||||||
|
class=$(xdotool getwindowclassname "$(xdotool getwindowfocus)")
|
||||||
|
|
||||||
|
# Semicolon-separated list of terminal class names.
|
||||||
|
terminals='Gnome-terminal;Xfce4-terminal;konsole;xterm;URxvt;Terminator;Alacritty;kitty;wezterm'
|
||||||
|
|
||||||
|
# Determine the keystroke based on the window type and the argument passed.
|
||||||
|
if echo "$terminals" | grep -q "$class"; then
|
||||||
|
# This is a terminal, so use Shift.
|
||||||
|
if [ "$1" = "copy" ]; then
|
||||||
|
xdotool key --clearmodifiers ctrl+shift+c
|
||||||
|
elif [ "$1" = "paste" ]; then
|
||||||
|
xdotool key --clearmodifiers ctrl+shift+v
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# This is a standard GUI app.
|
||||||
|
if [ "$1" = "copy" ]; then
|
||||||
|
xdotool key --clearmodifiers ctrl+c
|
||||||
|
elif [ "$1" = "paste" ]; then
|
||||||
|
xdotool key --clearmodifiers ctrl+v
|
||||||
|
fi
|
||||||
|
fi
|
||||||
@@ -7,7 +7,7 @@ fi
|
|||||||
|
|
||||||
if [ `whoami` != "root" ] ; then
|
if [ `whoami` != "root" ] ; then
|
||||||
if which sudo >/dev/null 2>&1 ; then
|
if which sudo >/dev/null 2>&1 ; then
|
||||||
sudo $0 $*
|
sudo "$0" "$@"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
echo "Sorry, this requires root." >&2
|
echo "Sorry, this requires root." >&2
|
||||||
546
bin/macos/chromebundles.py
Executable file
546
bin/macos/chromebundles.py
Executable file
@@ -0,0 +1,546 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import plistlib
|
||||||
|
import shutil
|
||||||
|
import stat
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PIL import Image, ImageEnhance
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_CHROME_APP = Path("/Applications/Google Chrome.app")
|
||||||
|
DEFAULT_APPS_DIR = Path.home() / "Applications" / "Chrome Containers"
|
||||||
|
|
||||||
|
# Edit this list for your containers.
|
||||||
|
CONTAINERS = [
|
||||||
|
# {
|
||||||
|
# "name": "Chrome Work",
|
||||||
|
# "bundle_id": "com.example.chrome.work",
|
||||||
|
# "mode": "persistent",
|
||||||
|
# "profile_dir": str(Path.home() / ".chrome-work"),
|
||||||
|
# "badge_path": str(Path.home() / ".chrome-container-badges" / "briefcase.svg"),
|
||||||
|
# },
|
||||||
|
{
|
||||||
|
"name": "Chrome Family",
|
||||||
|
"bundle_id": "com.example.chrome.family",
|
||||||
|
"mode": "persistent",
|
||||||
|
"profile_dir": str(Path.home() / ".chrome-family"),
|
||||||
|
"badge_path": str(Path.home() / ".chrome-container-badges" / "family.svg"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Chrome Research",
|
||||||
|
"bundle_id": "com.example.chrome.research",
|
||||||
|
"mode": "persistent",
|
||||||
|
"profile_dir": str(Path.home() / ".chrome-research"),
|
||||||
|
"badge_path": str(Path.home() / ".chrome-container-badges" / "research.svg"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Chrome Ephemeral",
|
||||||
|
"bundle_id": "com.example.chrome.ephemeral",
|
||||||
|
"mode": "ephemeral",
|
||||||
|
"profile_dir": None,
|
||||||
|
"badge_path": str(Path.home() / ".chrome-container-badges" / "fire.svg"),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
DEFAULT_COLOR_FACTOR = 0.55
|
||||||
|
DEFAULT_BRIGHTNESS_FACTOR = 0.94
|
||||||
|
DEFAULT_CONTRAST_FACTOR = 0.97
|
||||||
|
DEFAULT_BADGE_FRACTION = 0.50
|
||||||
|
DEFAULT_PADDING_FRACTION = 0.03
|
||||||
|
DEFAULT_BADGE_OPACITY = 0.96
|
||||||
|
|
||||||
|
|
||||||
|
def run(cmd, check=True, capture_output=False, text=True):
|
||||||
|
return subprocess.run(cmd, check=check, capture_output=capture_output, text=text)
|
||||||
|
|
||||||
|
|
||||||
|
def require_tool(name: str):
|
||||||
|
if shutil.which(name) is None:
|
||||||
|
print(f"Missing required tool: {name}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_icon_size(path: Path):
|
||||||
|
name = path.name
|
||||||
|
if not name.endswith(".png") or not name.startswith("icon_"):
|
||||||
|
return (0, 0)
|
||||||
|
|
||||||
|
stem = name[:-4]
|
||||||
|
rest = stem[len("icon_"):]
|
||||||
|
scale = 1
|
||||||
|
if rest.endswith("@2x"):
|
||||||
|
rest = rest[:-3]
|
||||||
|
scale = 2
|
||||||
|
|
||||||
|
try:
|
||||||
|
left, right = rest.split("x", 1)
|
||||||
|
return (int(left) * scale, int(right) * scale)
|
||||||
|
except Exception:
|
||||||
|
return (0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def find_source_icns(app_path: Path) -> Path:
|
||||||
|
info_plist = app_path / "Contents" / "Info.plist"
|
||||||
|
resources_dir = app_path / "Contents" / "Resources"
|
||||||
|
|
||||||
|
if not info_plist.exists():
|
||||||
|
raise FileNotFoundError(f"Missing Info.plist: {info_plist}")
|
||||||
|
if not resources_dir.exists():
|
||||||
|
raise FileNotFoundError(f"Missing Resources directory: {resources_dir}")
|
||||||
|
|
||||||
|
with info_plist.open("rb") as f:
|
||||||
|
plist = plistlib.load(f)
|
||||||
|
|
||||||
|
icon_name = plist.get("CFBundleIconFile")
|
||||||
|
if icon_name:
|
||||||
|
if not icon_name.endswith(".icns"):
|
||||||
|
icon_name += ".icns"
|
||||||
|
candidate = resources_dir / icon_name
|
||||||
|
if candidate.exists():
|
||||||
|
return candidate
|
||||||
|
|
||||||
|
chrome_named = sorted(resources_dir.glob("*[Cc]hrome*.icns"))
|
||||||
|
if chrome_named:
|
||||||
|
return chrome_named[0]
|
||||||
|
|
||||||
|
any_icns = sorted(resources_dir.glob("*.icns"))
|
||||||
|
if any_icns:
|
||||||
|
return any_icns[0]
|
||||||
|
|
||||||
|
raise FileNotFoundError(f"No .icns file found in {resources_dir}")
|
||||||
|
|
||||||
|
|
||||||
|
def extract_iconset(icns_path: Path, out_iconset: Path):
|
||||||
|
run(["iconutil", "-c", "iconset", str(icns_path), "-o", str(out_iconset)])
|
||||||
|
|
||||||
|
|
||||||
|
def largest_png(iconset_dir: Path) -> Path:
|
||||||
|
pngs = list(iconset_dir.glob("*.png"))
|
||||||
|
if not pngs:
|
||||||
|
raise FileNotFoundError(f"No PNGs found in {iconset_dir}")
|
||||||
|
pngs.sort(key=lambda p: parse_icon_size(p)[0] * parse_icon_size(p)[1], reverse=True)
|
||||||
|
return pngs[0]
|
||||||
|
|
||||||
|
|
||||||
|
def rasterize_svg(svg_path: Path, out_png: Path, size: int = 1024):
|
||||||
|
# Prefer librsvg if installed.
|
||||||
|
if shutil.which("rsvg-convert"):
|
||||||
|
run([
|
||||||
|
"rsvg-convert",
|
||||||
|
"-w", str(size),
|
||||||
|
"-h", str(size),
|
||||||
|
"-o", str(out_png),
|
||||||
|
str(svg_path),
|
||||||
|
])
|
||||||
|
return
|
||||||
|
|
||||||
|
# Fallback to Inkscape CLI if available.
|
||||||
|
if shutil.which("inkscape"):
|
||||||
|
run([
|
||||||
|
"inkscape",
|
||||||
|
str(svg_path),
|
||||||
|
"--export-type=png",
|
||||||
|
f"--export-filename={out_png}",
|
||||||
|
"-w", str(size),
|
||||||
|
"-h", str(size),
|
||||||
|
])
|
||||||
|
return
|
||||||
|
|
||||||
|
raise RuntimeError(
|
||||||
|
f"SVG badge provided but no SVG rasterizer found for {svg_path}. "
|
||||||
|
"Install librsvg (rsvg-convert) or Inkscape."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_badge_image(badge_path: Path, temp_dir: Path) -> Image.Image | None:
|
||||||
|
if not badge_path.exists():
|
||||||
|
print(f"Warning: badge file not found, skipping overlay: {badge_path}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
suffix = badge_path.suffix.lower()
|
||||||
|
|
||||||
|
if suffix == ".png":
|
||||||
|
return Image.open(badge_path).convert("RGBA")
|
||||||
|
|
||||||
|
if suffix == ".svg":
|
||||||
|
rasterized = temp_dir / f"{badge_path.stem}.png"
|
||||||
|
rasterize_svg(badge_path, rasterized, size=1024)
|
||||||
|
return Image.open(rasterized).convert("RGBA")
|
||||||
|
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Unsupported badge format for {badge_path}. "
|
||||||
|
"Supported formats: .png, .svg"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def compose_icon(
|
||||||
|
base_png: Path,
|
||||||
|
badge_path: str | None,
|
||||||
|
out_master: Path,
|
||||||
|
color_factor: float,
|
||||||
|
brightness_factor: float,
|
||||||
|
contrast_factor: float,
|
||||||
|
badge_fraction: float,
|
||||||
|
padding_fraction: float,
|
||||||
|
badge_opacity: float,
|
||||||
|
temp_dir: Path,
|
||||||
|
):
|
||||||
|
base = Image.open(base_png).convert("RGBA")
|
||||||
|
|
||||||
|
muted = ImageEnhance.Color(base).enhance(color_factor)
|
||||||
|
muted = ImageEnhance.Brightness(muted).enhance(brightness_factor)
|
||||||
|
muted = ImageEnhance.Contrast(muted).enhance(contrast_factor)
|
||||||
|
|
||||||
|
result = muted.copy()
|
||||||
|
|
||||||
|
if badge_path:
|
||||||
|
badge = load_badge_image(Path(badge_path).expanduser(), temp_dir)
|
||||||
|
if badge is not None:
|
||||||
|
w, h = result.size
|
||||||
|
|
||||||
|
max_badge_w = int(w * badge_fraction)
|
||||||
|
max_badge_h = int(h * badge_fraction)
|
||||||
|
pad = max(4, int(w * padding_fraction))
|
||||||
|
|
||||||
|
bw, bh = badge.size
|
||||||
|
scale = min(max_badge_w / bw, max_badge_h / bh)
|
||||||
|
new_size = (max(1, int(bw * scale)), max(1, int(bh * scale)))
|
||||||
|
badge = badge.resize(new_size, Image.LANCZOS)
|
||||||
|
|
||||||
|
if badge_opacity < 1.0:
|
||||||
|
alpha = badge.getchannel("A")
|
||||||
|
alpha = ImageEnhance.Brightness(alpha).enhance(badge_opacity)
|
||||||
|
badge.putalpha(alpha)
|
||||||
|
|
||||||
|
x = w - badge.width - pad
|
||||||
|
y = h - badge.height - pad
|
||||||
|
result.alpha_composite(badge, (x, y))
|
||||||
|
|
||||||
|
result.save(out_master)
|
||||||
|
|
||||||
|
|
||||||
|
def build_iconset_from_master(master_png: Path, out_iconset: Path):
|
||||||
|
out_iconset.mkdir(parents=True, exist_ok=True)
|
||||||
|
img = Image.open(master_png).convert("RGBA")
|
||||||
|
|
||||||
|
sizes = [
|
||||||
|
("icon_16x16.png", 16),
|
||||||
|
("icon_16x16@2x.png", 32),
|
||||||
|
("icon_32x32.png", 32),
|
||||||
|
("icon_32x32@2x.png", 64),
|
||||||
|
("icon_128x128.png", 128),
|
||||||
|
("icon_128x128@2x.png", 256),
|
||||||
|
("icon_256x256.png", 256),
|
||||||
|
("icon_256x256@2x.png", 512),
|
||||||
|
("icon_512x512.png", 512),
|
||||||
|
("icon_512x512@2x.png", 1024),
|
||||||
|
]
|
||||||
|
|
||||||
|
for filename, size in sizes:
|
||||||
|
resized = img.resize((size, size), Image.LANCZOS)
|
||||||
|
resized.save(out_iconset / filename)
|
||||||
|
|
||||||
|
|
||||||
|
def iconset_to_icns(iconset_dir: Path, out_icns: Path):
|
||||||
|
run(["iconutil", "-c", "icns", str(iconset_dir), "-o", str(out_icns)])
|
||||||
|
|
||||||
|
|
||||||
|
def make_launch_script(chrome_bin: Path, mode: str, profile_dir: str | None) -> str:
|
||||||
|
chrome_bin_escaped = str(chrome_bin).replace("\\", "\\\\").replace('"', '\\"')
|
||||||
|
|
||||||
|
if mode == "persistent":
|
||||||
|
if not profile_dir:
|
||||||
|
raise ValueError("Persistent container requires profile_dir")
|
||||||
|
profile_dir_escaped = profile_dir.replace("\\", "\\\\").replace('"', '\\"')
|
||||||
|
return f"""#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CHROME_BIN="{chrome_bin_escaped}"
|
||||||
|
PROFILE_DIR="{profile_dir_escaped}"
|
||||||
|
|
||||||
|
mkdir -p "$PROFILE_DIR"
|
||||||
|
|
||||||
|
exec "$CHROME_BIN" \\
|
||||||
|
--user-data-dir="$PROFILE_DIR" \\
|
||||||
|
--no-first-run \\
|
||||||
|
--no-default-browser-check \\
|
||||||
|
--new-window
|
||||||
|
"""
|
||||||
|
elif mode == "ephemeral":
|
||||||
|
return f"""#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CHROME_BIN="{chrome_bin_escaped}"
|
||||||
|
PROFILE_DIR="$(mktemp -d /tmp/chrome-ephemeral-XXXXXX)"
|
||||||
|
|
||||||
|
cleanup() {{
|
||||||
|
rm -rf "$PROFILE_DIR"
|
||||||
|
}}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
exec "$CHROME_BIN" \\
|
||||||
|
--user-data-dir="$PROFILE_DIR" \\
|
||||||
|
--no-first-run \\
|
||||||
|
--no-default-browser-check \\
|
||||||
|
--new-window
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown mode: {mode}")
|
||||||
|
|
||||||
|
|
||||||
|
def write_plist(app_name: str, bundle_id: str, plist_path: Path):
|
||||||
|
plist = {
|
||||||
|
"CFBundleDisplayName": app_name,
|
||||||
|
"CFBundleExecutable": "launch",
|
||||||
|
"CFBundleIdentifier": bundle_id,
|
||||||
|
"CFBundleName": app_name,
|
||||||
|
"CFBundlePackageType": "APPL",
|
||||||
|
"CFBundleShortVersionString": "1.0",
|
||||||
|
"CFBundleVersion": "1",
|
||||||
|
"LSMinimumSystemVersion": "12.0",
|
||||||
|
"NSHighResolutionCapable": True,
|
||||||
|
"CFBundleIconFile": "applet",
|
||||||
|
}
|
||||||
|
with plist_path.open("wb") as f:
|
||||||
|
plistlib.dump(plist, f)
|
||||||
|
|
||||||
|
|
||||||
|
def codesign_app(app_dir: Path):
|
||||||
|
try:
|
||||||
|
run(["/usr/bin/codesign", "--force", "--deep", "--sign", "-", str(app_dir)])
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"Warning: codesign failed for {app_dir}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_container(container: dict) -> dict:
|
||||||
|
required = ["name", "bundle_id", "mode"]
|
||||||
|
for key in required:
|
||||||
|
if key not in container or not container[key]:
|
||||||
|
raise ValueError(f"Container missing required key: {key}")
|
||||||
|
|
||||||
|
mode = container["mode"]
|
||||||
|
if mode not in {"persistent", "ephemeral"}:
|
||||||
|
raise ValueError(f"Invalid mode for {container['name']}: {mode}")
|
||||||
|
|
||||||
|
if mode == "persistent" and not container.get("profile_dir"):
|
||||||
|
raise ValueError(f"Persistent container missing profile_dir: {container['name']}")
|
||||||
|
|
||||||
|
return container
|
||||||
|
|
||||||
|
|
||||||
|
def container_matches_filter(name: str, only_names: set[str]) -> bool:
|
||||||
|
if not only_names:
|
||||||
|
return True
|
||||||
|
return name in only_names
|
||||||
|
|
||||||
|
|
||||||
|
def build_icon_for_app(
|
||||||
|
source_icns: Path,
|
||||||
|
badge_path: str | None,
|
||||||
|
out_icns: Path,
|
||||||
|
color_factor: float,
|
||||||
|
brightness_factor: float,
|
||||||
|
contrast_factor: float,
|
||||||
|
badge_fraction: float,
|
||||||
|
padding_fraction: float,
|
||||||
|
badge_opacity: float,
|
||||||
|
):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
tmpdir = Path(tmp)
|
||||||
|
base_iconset = tmpdir / "base.iconset"
|
||||||
|
new_iconset = tmpdir / "new.iconset"
|
||||||
|
master_png = tmpdir / "master.png"
|
||||||
|
|
||||||
|
extract_iconset(source_icns, base_iconset)
|
||||||
|
base_png = largest_png(base_iconset)
|
||||||
|
size = parse_icon_size(base_png)
|
||||||
|
print(f" Base icon source: {base_png.name} ({size[0]}x{size[1]})")
|
||||||
|
|
||||||
|
compose_icon(
|
||||||
|
base_png=base_png,
|
||||||
|
badge_path=badge_path,
|
||||||
|
out_master=master_png,
|
||||||
|
color_factor=color_factor,
|
||||||
|
brightness_factor=brightness_factor,
|
||||||
|
contrast_factor=contrast_factor,
|
||||||
|
badge_fraction=badge_fraction,
|
||||||
|
padding_fraction=padding_fraction,
|
||||||
|
badge_opacity=badge_opacity,
|
||||||
|
temp_dir=tmpdir,
|
||||||
|
)
|
||||||
|
build_iconset_from_master(master_png, new_iconset)
|
||||||
|
iconset_to_icns(new_iconset, out_icns)
|
||||||
|
|
||||||
|
|
||||||
|
def create_or_update_container(
|
||||||
|
container: dict,
|
||||||
|
apps_dir: Path,
|
||||||
|
chrome_bin: Path,
|
||||||
|
source_icns: Path,
|
||||||
|
force: bool,
|
||||||
|
update_icons_only: bool,
|
||||||
|
codesign: bool,
|
||||||
|
color_factor: float,
|
||||||
|
brightness_factor: float,
|
||||||
|
contrast_factor: float,
|
||||||
|
badge_fraction: float,
|
||||||
|
padding_fraction: float,
|
||||||
|
badge_opacity: float,
|
||||||
|
):
|
||||||
|
app_name = container["name"]
|
||||||
|
bundle_id = container["bundle_id"]
|
||||||
|
mode = container["mode"]
|
||||||
|
profile_dir = container.get("profile_dir")
|
||||||
|
badge_path = container.get("badge_path")
|
||||||
|
|
||||||
|
app_dir = apps_dir / f"{app_name}.app"
|
||||||
|
contents_dir = app_dir / "Contents"
|
||||||
|
macos_dir = contents_dir / "MacOS"
|
||||||
|
resources_dir = contents_dir / "Resources"
|
||||||
|
out_icns = resources_dir / "applet.icns"
|
||||||
|
|
||||||
|
exists = app_dir.exists()
|
||||||
|
|
||||||
|
if update_icons_only:
|
||||||
|
if not exists:
|
||||||
|
print(f"Skipping missing app for icon update: {app_dir}")
|
||||||
|
return
|
||||||
|
print(f"Updating icon only for {app_name}...")
|
||||||
|
resources_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
build_icon_for_app(
|
||||||
|
source_icns,
|
||||||
|
badge_path,
|
||||||
|
out_icns,
|
||||||
|
color_factor,
|
||||||
|
brightness_factor,
|
||||||
|
contrast_factor,
|
||||||
|
badge_fraction,
|
||||||
|
padding_fraction,
|
||||||
|
badge_opacity,
|
||||||
|
)
|
||||||
|
if codesign:
|
||||||
|
codesign_app(app_dir)
|
||||||
|
print(f" Updated icon: {out_icns}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if exists and not force:
|
||||||
|
print(f"Skipping existing app: {app_dir}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if exists and force:
|
||||||
|
print(f"Recreating existing app: {app_dir}")
|
||||||
|
shutil.rmtree(app_dir)
|
||||||
|
else:
|
||||||
|
print(f"Creating {app_name}...")
|
||||||
|
|
||||||
|
macos_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
resources_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
write_plist(app_name, bundle_id, contents_dir / "Info.plist")
|
||||||
|
|
||||||
|
launch_script = make_launch_script(chrome_bin, mode, profile_dir)
|
||||||
|
launch_path = macos_dir / "launch"
|
||||||
|
launch_path.write_text(launch_script, encoding="utf-8")
|
||||||
|
launch_path.chmod(launch_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
||||||
|
|
||||||
|
if mode == "persistent":
|
||||||
|
Path(profile_dir).expanduser().mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
build_icon_for_app(
|
||||||
|
source_icns,
|
||||||
|
badge_path,
|
||||||
|
out_icns,
|
||||||
|
color_factor,
|
||||||
|
brightness_factor,
|
||||||
|
contrast_factor,
|
||||||
|
badge_fraction,
|
||||||
|
padding_fraction,
|
||||||
|
badge_opacity,
|
||||||
|
)
|
||||||
|
|
||||||
|
if codesign:
|
||||||
|
codesign_app(app_dir)
|
||||||
|
|
||||||
|
print(f" Created: {app_dir}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Create and manage Chrome container wrapper apps on macOS.")
|
||||||
|
parser.add_argument("--chrome-app", default=str(DEFAULT_CHROME_APP), help="Path to Chrome app bundle")
|
||||||
|
parser.add_argument("--apps-dir", default=str(DEFAULT_APPS_DIR), help="Directory for generated wrapper apps")
|
||||||
|
parser.add_argument("--force", action="store_true", help="Recreate containers even if they already exist")
|
||||||
|
parser.add_argument("--update-icons-only", action="store_true", help="Only rebuild icons for existing containers")
|
||||||
|
parser.add_argument("--no-codesign", action="store_true", help="Skip ad-hoc codesigning")
|
||||||
|
parser.add_argument(
|
||||||
|
"--only",
|
||||||
|
action="append",
|
||||||
|
default=[],
|
||||||
|
help="Limit to specific container name; can be passed multiple times",
|
||||||
|
)
|
||||||
|
parser.add_argument("--color-factor", type=float, default=DEFAULT_COLOR_FACTOR)
|
||||||
|
parser.add_argument("--brightness-factor", type=float, default=DEFAULT_BRIGHTNESS_FACTOR)
|
||||||
|
parser.add_argument("--contrast-factor", type=float, default=DEFAULT_CONTRAST_FACTOR)
|
||||||
|
parser.add_argument("--badge-fraction", type=float, default=DEFAULT_BADGE_FRACTION)
|
||||||
|
parser.add_argument("--padding-fraction", type=float, default=DEFAULT_PADDING_FRACTION)
|
||||||
|
parser.add_argument("--badge-opacity", type=float, default=DEFAULT_BADGE_OPACITY)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
require_tool("iconutil")
|
||||||
|
|
||||||
|
chrome_app = Path(args.chrome_app).expanduser().resolve()
|
||||||
|
apps_dir = Path(args.apps_dir).expanduser().resolve()
|
||||||
|
chrome_bin = chrome_app / "Contents" / "MacOS" / "Google Chrome"
|
||||||
|
|
||||||
|
if not chrome_bin.exists():
|
||||||
|
print(f"Chrome binary not found: {chrome_bin}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import PIL # noqa: F401
|
||||||
|
except ImportError:
|
||||||
|
print("Pillow is required. Install it with:", file=sys.stderr)
|
||||||
|
print(" python3 -m pip install --user pillow", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
source_icns = find_source_icns(chrome_app)
|
||||||
|
apps_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
only_names = set(args.only)
|
||||||
|
|
||||||
|
print(f"Using Python: {sys.executable}")
|
||||||
|
print(f"Using Chrome app: {chrome_app}")
|
||||||
|
print(f"Using source icon: {source_icns}")
|
||||||
|
print(f"Apps directory: {apps_dir}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
for raw_container in CONTAINERS:
|
||||||
|
container = sanitize_container(raw_container)
|
||||||
|
if not container_matches_filter(container["name"], only_names):
|
||||||
|
continue
|
||||||
|
|
||||||
|
create_or_update_container(
|
||||||
|
container=container,
|
||||||
|
apps_dir=apps_dir,
|
||||||
|
chrome_bin=chrome_bin,
|
||||||
|
source_icns=source_icns,
|
||||||
|
force=args.force,
|
||||||
|
update_icons_only=args.update_icons_only,
|
||||||
|
codesign=not args.no_codesign,
|
||||||
|
color_factor=args.color_factor,
|
||||||
|
brightness_factor=args.brightness_factor,
|
||||||
|
contrast_factor=args.contrast_factor,
|
||||||
|
badge_fraction=args.badge_fraction,
|
||||||
|
padding_fraction=args.padding_fraction,
|
||||||
|
badge_opacity=args.badge_opacity,
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
304
bin/macos/update_brewfile
Executable file
304
bin/macos/update_brewfile
Executable file
@@ -0,0 +1,304 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import difflib
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
# Regex to match brew/cask/tap/mas lines
|
||||||
|
PKG_RE = re.compile(r'^\s*(brew|cask|tap|mas)\s+["\']([^"\']+)["\'](.*)$')
|
||||||
|
|
||||||
|
def colorize_diff(lines):
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith('+') and not line.startswith('+++'):
|
||||||
|
yield f"\033[32m{line}\033[0m"
|
||||||
|
elif line.startswith('-') and not line.startswith('---'):
|
||||||
|
yield f"\033[31m{line}\033[0m"
|
||||||
|
elif line.startswith('^'):
|
||||||
|
yield f"\033[36m{line}\033[0m"
|
||||||
|
else:
|
||||||
|
yield line
|
||||||
|
|
||||||
|
class Entry:
|
||||||
|
def sort_key(self): raise NotImplementedError()
|
||||||
|
def to_lines(self): raise NotImplementedError()
|
||||||
|
|
||||||
|
class PackageEntry(Entry):
|
||||||
|
def __init__(self, pkg_type, name, options, comments=None):
|
||||||
|
self.pkg_type = pkg_type
|
||||||
|
self.name = name
|
||||||
|
self.options = options.strip()
|
||||||
|
self.comments = comments or []
|
||||||
|
|
||||||
|
def sort_key(self):
|
||||||
|
order = {'tap': 0, 'brew': 1, 'cask': 2, 'mas': 3}
|
||||||
|
return (order.get(self.pkg_type, 4), self.name)
|
||||||
|
|
||||||
|
def to_lines(self):
|
||||||
|
res = list(self.comments)
|
||||||
|
pkg_line = f'{self.pkg_type} "{self.name}"'
|
||||||
|
if self.options:
|
||||||
|
if not self.options.startswith(','):
|
||||||
|
pkg_line += ' '
|
||||||
|
pkg_line += self.options
|
||||||
|
res.append(pkg_line)
|
||||||
|
return res
|
||||||
|
|
||||||
|
class TextEntry(Entry):
|
||||||
|
def __init__(self, lines, is_header=True):
|
||||||
|
self.lines = lines
|
||||||
|
self.is_header = is_header
|
||||||
|
|
||||||
|
def sort_key(self):
|
||||||
|
# Header is -1, Trailing is 5 (after all package types 0-4)
|
||||||
|
return (-1 if self.is_header else 5, "")
|
||||||
|
|
||||||
|
def to_lines(self):
|
||||||
|
return self.lines
|
||||||
|
|
||||||
|
def get_repo_root():
|
||||||
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
try:
|
||||||
|
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'],
|
||||||
|
cwd=script_dir,
|
||||||
|
stderr=subprocess.STDOUT).decode().strip()
|
||||||
|
return root
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
# If not in a git repo, go up 2 levels from script_dir (bin/macos/)
|
||||||
|
return os.path.abspath(os.path.join(script_dir, '..', '..'))
|
||||||
|
|
||||||
|
def get_ignore_list(repo_root):
|
||||||
|
ignore = set()
|
||||||
|
paths = [
|
||||||
|
os.path.join(repo_root, '.Brewfile.ignore'),
|
||||||
|
os.path.expanduser('~/.Brewfile.ignore'),
|
||||||
|
os.path.expanduser('~/.config/homebrew/ignore')
|
||||||
|
]
|
||||||
|
for path in paths:
|
||||||
|
if os.path.exists(path):
|
||||||
|
with open(path) as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.split('#')[0].strip()
|
||||||
|
if line:
|
||||||
|
ignore.add(line)
|
||||||
|
return ignore
|
||||||
|
|
||||||
|
def get_current_packages(args):
|
||||||
|
"""Runs brew bundle dump and returns lines."""
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["HOMEBREW_NO_AUTO_UPDATE"] = "1"
|
||||||
|
env["HOMEBREW_NO_INSTALL_CLEANUP"] = "1"
|
||||||
|
env["HOMEBREW_NO_ENV_HINTS"] = "1"
|
||||||
|
|
||||||
|
cmd = ['brew', 'bundle', 'dump', '--file=-']
|
||||||
|
if args.no_mas: cmd.append('--no-mas')
|
||||||
|
if args.no_vscode: cmd.append('--no-vscode')
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(cmd, env=env, stderr=subprocess.DEVNULL).decode()
|
||||||
|
return output.splitlines()
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print("Error: 'brew bundle dump' failed. Is homebrew installed?", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def parse_brewfile(content):
|
||||||
|
"""
|
||||||
|
Parses Brewfile content.
|
||||||
|
Returns:
|
||||||
|
- entries: list of Entry objects
|
||||||
|
- conditional_pkgs: set of (type, name)
|
||||||
|
- footer: string (everything from first conditional onwards)
|
||||||
|
"""
|
||||||
|
lines = content.splitlines()
|
||||||
|
conditional_pkgs = set()
|
||||||
|
|
||||||
|
# Find the start of the first conditional block
|
||||||
|
first_conditional_idx = -1
|
||||||
|
in_conditional = 0
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
stripped = line.strip()
|
||||||
|
if stripped.startswith(('if ', 'unless ', 'case ', 'def ', 'begin ')) and not stripped.endswith('; end'):
|
||||||
|
if first_conditional_idx == -1:
|
||||||
|
# Look back for comments that might belong to this block
|
||||||
|
j = i - 1
|
||||||
|
while j >= 0 and (lines[j].strip().startswith('#') or not lines[j].strip()):
|
||||||
|
j -= 1
|
||||||
|
first_conditional_idx = j + 1
|
||||||
|
in_conditional += 1
|
||||||
|
|
||||||
|
if in_conditional > 0:
|
||||||
|
match = PKG_RE.match(line)
|
||||||
|
if match:
|
||||||
|
conditional_pkgs.add((match.group(1), match.group(2)))
|
||||||
|
|
||||||
|
if stripped == 'end' or stripped.endswith('; end'):
|
||||||
|
in_conditional -= 1
|
||||||
|
|
||||||
|
unconditional_lines = lines[:first_conditional_idx] if first_conditional_idx != -1 else lines
|
||||||
|
footer = "\n".join(lines[first_conditional_idx:]) if first_conditional_idx != -1 else ""
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
comment_buffer = []
|
||||||
|
first_pkg_seen = False
|
||||||
|
|
||||||
|
for line in unconditional_lines:
|
||||||
|
match = PKG_RE.match(line)
|
||||||
|
if match:
|
||||||
|
if not first_pkg_seen:
|
||||||
|
if comment_buffer:
|
||||||
|
entries.append(TextEntry(comment_buffer, is_header=True))
|
||||||
|
comment_buffer = []
|
||||||
|
first_pkg_seen = True
|
||||||
|
entries.append(PackageEntry(match.group(1), match.group(2), match.group(3), comment_buffer))
|
||||||
|
comment_buffer = []
|
||||||
|
else:
|
||||||
|
comment_buffer.append(line)
|
||||||
|
|
||||||
|
if comment_buffer:
|
||||||
|
entries.append(TextEntry(comment_buffer, is_header=False))
|
||||||
|
|
||||||
|
return entries, conditional_pkgs, footer
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
if sys.platform != "darwin":
|
||||||
|
print(f"Warning: Running on {sys.platform}. Brewfile is primarily for macOS.", file=sys.stderr)
|
||||||
|
|
||||||
|
repo_root = get_repo_root()
|
||||||
|
brewfile_path = os.path.join(repo_root, 'Brewfile')
|
||||||
|
|
||||||
|
if not os.path.exists(brewfile_path):
|
||||||
|
print(f"Error: Brewfile not found at {brewfile_path}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
with open(brewfile_path) as f:
|
||||||
|
old_content = f.read()
|
||||||
|
|
||||||
|
old_entries, conditional_pkgs, footer = parse_brewfile(old_content)
|
||||||
|
ignore_list = get_ignore_list(repo_root)
|
||||||
|
|
||||||
|
old_pkg_map = {}
|
||||||
|
for e in old_entries:
|
||||||
|
if isinstance(e, PackageEntry):
|
||||||
|
key = (e.pkg_type, e.name)
|
||||||
|
if key not in old_pkg_map:
|
||||||
|
old_pkg_map[key] = e
|
||||||
|
|
||||||
|
dumped_lines = get_current_packages(args)
|
||||||
|
dumped_pkgs = []
|
||||||
|
for line in dumped_lines:
|
||||||
|
match = PKG_RE.match(line)
|
||||||
|
if match:
|
||||||
|
pkg_type, pkg_name, pkg_options = match.group(1), match.group(2), match.group(3)
|
||||||
|
if pkg_name in ignore_list or (pkg_type, pkg_name) in conditional_pkgs:
|
||||||
|
continue
|
||||||
|
dumped_pkgs.append(PackageEntry(pkg_type, pkg_name, pkg_options))
|
||||||
|
|
||||||
|
new_entries = []
|
||||||
|
for e in old_entries:
|
||||||
|
if isinstance(e, TextEntry) and e.is_header:
|
||||||
|
new_entries.append(e)
|
||||||
|
|
||||||
|
seen_in_new = set()
|
||||||
|
added_count = 0
|
||||||
|
removed_count = 0
|
||||||
|
merged_count = 0
|
||||||
|
|
||||||
|
if args.add_only:
|
||||||
|
for e in old_entries:
|
||||||
|
if isinstance(e, PackageEntry):
|
||||||
|
new_entries.append(e)
|
||||||
|
seen_in_new.add((e.pkg_type, e.name))
|
||||||
|
for d in dumped_pkgs:
|
||||||
|
if (d.pkg_type, d.name) not in seen_in_new:
|
||||||
|
new_entries.append(d)
|
||||||
|
seen_in_new.add((d.pkg_type, d.name))
|
||||||
|
added_count += 1
|
||||||
|
else:
|
||||||
|
for d in dumped_pkgs:
|
||||||
|
key = (d.pkg_type, d.name)
|
||||||
|
if key in seen_in_new: continue
|
||||||
|
if key in old_pkg_map:
|
||||||
|
merged = old_pkg_map[key]
|
||||||
|
if not merged.options:
|
||||||
|
merged.options = d.options
|
||||||
|
new_entries.append(merged)
|
||||||
|
merged_count += 1
|
||||||
|
else:
|
||||||
|
new_entries.append(d)
|
||||||
|
added_count += 1
|
||||||
|
seen_in_new.add(key)
|
||||||
|
|
||||||
|
# Check for removals
|
||||||
|
for key in old_pkg_map:
|
||||||
|
if key not in seen_in_new:
|
||||||
|
removed_count += 1
|
||||||
|
|
||||||
|
for e in old_entries:
|
||||||
|
if isinstance(e, TextEntry) and not e.is_header:
|
||||||
|
new_entries.append(e)
|
||||||
|
|
||||||
|
new_entries.sort(key=lambda x: x.sort_key())
|
||||||
|
|
||||||
|
output_lines = []
|
||||||
|
last_type = None
|
||||||
|
for e in new_entries:
|
||||||
|
if isinstance(e, PackageEntry):
|
||||||
|
if last_type and e.pkg_type != last_type:
|
||||||
|
output_lines.append("")
|
||||||
|
last_type = e.pkg_type
|
||||||
|
|
||||||
|
lines = e.to_lines()
|
||||||
|
# If we just added a blank line, and the new lines start with one, skip the first new line
|
||||||
|
if output_lines and output_lines[-1] == "" and lines and lines[0] == "":
|
||||||
|
output_lines.extend(lines[1:])
|
||||||
|
else:
|
||||||
|
output_lines.extend(lines)
|
||||||
|
|
||||||
|
new_content = "\n".join(output_lines)
|
||||||
|
if footer:
|
||||||
|
if output_lines and output_lines[-1].strip():
|
||||||
|
new_content += "\n\n"
|
||||||
|
new_content += footer.strip() + "\n"
|
||||||
|
else:
|
||||||
|
new_content += "\n"
|
||||||
|
|
||||||
|
if new_content == old_content:
|
||||||
|
print("Brewfile is already up to date.")
|
||||||
|
else:
|
||||||
|
if args.dry_run:
|
||||||
|
print("Changes detected (dry run):")
|
||||||
|
diff = list(difflib.unified_diff(
|
||||||
|
old_content.splitlines(keepends=True),
|
||||||
|
new_content.splitlines(keepends=True),
|
||||||
|
fromfile='Brewfile (original)',
|
||||||
|
tofile='Brewfile (new)'
|
||||||
|
))
|
||||||
|
if sys.stdout.isatty():
|
||||||
|
sys.stdout.writelines(colorize_diff(diff))
|
||||||
|
else:
|
||||||
|
sys.stdout.writelines(diff)
|
||||||
|
else:
|
||||||
|
dir_name = os.path.dirname(brewfile_path)
|
||||||
|
with tempfile.NamedTemporaryFile('w', dir=dir_name, delete=False) as tf:
|
||||||
|
tf.write(new_content)
|
||||||
|
tempname = tf.name
|
||||||
|
os.replace(tempname, brewfile_path)
|
||||||
|
print("Brewfile updated.")
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
print(f"Summary: {added_count} added, {removed_count} removed, {merged_count} kept/merged.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Update Brewfile while preserving conditionals.")
|
||||||
|
parser.add_argument("--dry-run", action="store_true", help="Show changes without applying them.")
|
||||||
|
parser.add_argument("--add-only", action="store_true", help="Only add missing entries, do not remove existing ones.")
|
||||||
|
parser.add_argument("--verbose", "-v", action="store_true", help="Print summary of changes.")
|
||||||
|
parser.add_argument("--no-mas", action="store_true", help="Do not include Mac App Store apps.")
|
||||||
|
parser.add_argument("--no-vscode", action="store_true", help="Do not include VSCode extensions.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
main(args)
|
||||||
46
bin/prune-broken-symlinks.sh
Executable file
46
bin/prune-broken-symlinks.sh
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# shellcheck disable=SC2039,SC2086
|
||||||
|
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
prune_broken_symlinks() {
|
||||||
|
ask=1
|
||||||
|
dir="."
|
||||||
|
|
||||||
|
if [ "${1:-}" = "-y" ]; then
|
||||||
|
ask=0
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${1:-}" ]; then
|
||||||
|
dir="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if there are any broken symlinks first
|
||||||
|
broken_links=$(find -L "$dir" -xdev -type l -print 2>/dev/null)
|
||||||
|
if [ -z "$broken_links" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$ask" -eq 1 ]; then
|
||||||
|
# Print broken links
|
||||||
|
echo "$broken_links"
|
||||||
|
|
||||||
|
printf "Delete these links? [y/N] "
|
||||||
|
read -r reply
|
||||||
|
case "$reply" in
|
||||||
|
[yY]*)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Aborted."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Perform deletion
|
||||||
|
find -L "$dir" -xdev -type l -exec rm -- {} + 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execute the function
|
||||||
|
prune_broken_symlinks "$@"
|
||||||
63
bin/quartz
Executable file
63
bin/quartz
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# QUARTZ_DIR search logic
|
||||||
|
if [ -z "${QUARTZ_DIR:-}" ]; then
|
||||||
|
if [ -f "quartz.config.ts" ]; then
|
||||||
|
QUARTZ_DIR="$PWD"
|
||||||
|
elif [ -d "$HOME/Personal/notes-quartz" ]; then
|
||||||
|
QUARTZ_DIR="$HOME/Personal/notes-quartz"
|
||||||
|
elif [ -d "$HOME/Projects/notes-quartz" ]; then
|
||||||
|
QUARTZ_DIR="$HOME/Projects/notes-quartz"
|
||||||
|
else
|
||||||
|
echo "Error: QUARTZ_DIR could not be found." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$QUARTZ_DIR" ]; then
|
||||||
|
echo "Error: QUARTZ_DIR '$QUARTZ_DIR' is not a directory." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# NOTES_DIR search logic
|
||||||
|
PARSE_NOTES_DIR=""
|
||||||
|
# Use a copy of args to find -d/--directory
|
||||||
|
ARGS=("$@")
|
||||||
|
for ((i=0; i<${#ARGS[@]}; i++)); do
|
||||||
|
if [[ "${ARGS[i]}" == "-d" || "${ARGS[i]}" == "--directory" ]]; then
|
||||||
|
if [[ $((i+1)) -lt ${#ARGS[@]} ]]; then
|
||||||
|
PARSE_NOTES_DIR="${ARGS[i+1]}"
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$PARSE_NOTES_DIR" ]; then
|
||||||
|
NOTES_DIR="$PARSE_NOTES_DIR"
|
||||||
|
elif [ -z "${NOTES_DIR:-}" ]; then
|
||||||
|
if [ -d "$HOME/Notes" ]; then
|
||||||
|
NOTES_DIR="$HOME/Notes"
|
||||||
|
elif [ -d "$HOME/Personal/notes" ]; then
|
||||||
|
NOTES_DIR="$HOME/Personal/notes"
|
||||||
|
elif [ -d "$HOME/Projects/notes" ]; then
|
||||||
|
NOTES_DIR="$HOME/Projects/notes"
|
||||||
|
else
|
||||||
|
echo "Error: NOTES_DIR could not be found." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$NOTES_DIR" ]; then
|
||||||
|
echo "Error: NOTES_DIR '$NOTES_DIR' is not a directory." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$QUARTZ_DIR"
|
||||||
|
|
||||||
|
# Run npx quartz
|
||||||
|
# Following the prompt's structure but using NOTES_DIR for the flag
|
||||||
|
# npx quartz ${argv[1]} --directory ${NOTES_DIR} "$@"
|
||||||
|
# We use ${1:-} for argv[1] to handle cases with no arguments.
|
||||||
|
npx quartz "${1:-}" --directory "$NOTES_DIR" "$@"
|
||||||
83
bin/resign_for_debug.sh
Executable file
83
bin/resign_for_debug.sh
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
FORCE=false
|
||||||
|
TARGET_PATH=""
|
||||||
|
DEST_PATH=""
|
||||||
|
|
||||||
|
# Parse flags (looking for -f)
|
||||||
|
while getopts "f" opt; do
|
||||||
|
case $opt in
|
||||||
|
f) FORCE=true ;;
|
||||||
|
*) echo "Usage: $0 [-f] <binary_name_or_path> [destination]"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND-1))
|
||||||
|
|
||||||
|
# Check for first argument
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Error: No binary specified."
|
||||||
|
echo "Usage: $0 [-f] <binary_name_or_path> [destination]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. Resolve the Source Binary
|
||||||
|
if [[ "$1" == *"/"* ]]; then
|
||||||
|
SOURCE_BIN="$1"
|
||||||
|
else
|
||||||
|
SOURCE_BIN=$(command -v "$1")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$SOURCE_BIN" ]; then
|
||||||
|
echo "Error: Could not find binary at '$1'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Determine Destination Path
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
DEST_PATH="$2"
|
||||||
|
# If destination is a directory, append the basename
|
||||||
|
if [ -d "$DEST_PATH" ]; then
|
||||||
|
DEST_PATH="${DEST_PATH%/}/$(basename "$SOURCE_BIN")"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# No destination given: create a temp directory
|
||||||
|
TMP_DIR=$(mktemp -d -t "debug_unlock_XXXXXX")
|
||||||
|
DEST_PATH="$TMP_DIR/$(basename "$SOURCE_BIN")"
|
||||||
|
echo "Notice: No destination provided. Using temp path: $DEST_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Check for Collision
|
||||||
|
if [ -f "$DEST_PATH" ] && [ "$FORCE" = false ]; then
|
||||||
|
echo "Error: Destination '$DEST_PATH' already exists. Use -f to overwrite."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Copy and Sign
|
||||||
|
cp -f "$SOURCE_BIN" "$DEST_PATH"
|
||||||
|
chmod +x "$DEST_PATH"
|
||||||
|
|
||||||
|
ENTITLEMENTS_FILE=$(mktemp)
|
||||||
|
cat <<EOF > "$ENTITLEMENTS_FILE"
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.get-task-allow</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Unlocking: $SOURCE_BIN -> $DEST_PATH"
|
||||||
|
codesign -s - --entitlements "$ENTITLEMENTS_FILE" -f "$DEST_PATH" 2>/dev/null
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "✅ Success! You can now debug: $DEST_PATH"
|
||||||
|
else
|
||||||
|
echo "❌ Error: Code signing failed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm "$ENTITLEMENTS_FILE"
|
||||||
26
bin/restic.sh
Executable file
26
bin/restic.sh
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Script to execute a restic backup script specific to the current hostname.
|
||||||
|
#
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# Get the current hostname
|
||||||
|
HOSTNAME=$(hostname)
|
||||||
|
|
||||||
|
# Define the directory where hostname-specific scripts are stored
|
||||||
|
RESTIC_SCRIPTS_DIR="${HOME}/bin/restic"
|
||||||
|
|
||||||
|
# Construct the full path to the hostname-specific script
|
||||||
|
HOST_SPECIFIC_SCRIPT="${RESTIC_SCRIPTS_DIR}/${HOSTNAME}"
|
||||||
|
|
||||||
|
# Check if the script exists and is executable
|
||||||
|
if [[ -f "${HOST_SPECIFIC_SCRIPT}" && -x "${HOST_SPECIFIC_SCRIPT}" ]]; then
|
||||||
|
echo "Executing restic script for hostname: ${HOSTNAME}"
|
||||||
|
"${HOST_SPECIFIC_SCRIPT}"
|
||||||
|
else
|
||||||
|
echo "Error: No executable restic script found for hostname '${HOSTNAME}' at '${HOST_SPECIFIC_SCRIPT}'." >&2
|
||||||
|
echo "Please create an executable script at that path if you want to use this functionality." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
165
bin/restic/baymax
Executable file
165
bin/restic/baymax
Executable file
@@ -0,0 +1,165 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# A script to backup a single-user MacBook using restic to either a local
|
||||||
|
# filesystem or Backblaze B2.
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
# Directory to be backed up.
|
||||||
|
# For this script, we assume the user's home directory.
|
||||||
|
SOURCE_DIR="${HOME}"
|
||||||
|
|
||||||
|
# Exclude file location. We'll create a default one next to the script.
|
||||||
|
EXCLUDE_FILE="$HOME/.restic_exclude.darwin"
|
||||||
|
|
||||||
|
# --- Functions ---
|
||||||
|
usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: $(basename "$0") [-l /path/to/repo | -b [bucket]] [-u UPLOAD_LIMIT]
|
||||||
|
|
||||||
|
A script to backup a single-user MacBook using restic.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-l <path> Backup to a local filesystem repository at the given path.
|
||||||
|
-b [bucket] Backup to a Backblaze B2 bucket. The bucket name is optional.
|
||||||
|
If not provided, it will be read from the B2_BUCKET_NAME
|
||||||
|
environment variable.
|
||||||
|
-u <limit> Limit the upload speed to the given value in KB/s.
|
||||||
|
-h Show this help message.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Main Script ---
|
||||||
|
# Check if restic is installed
|
||||||
|
if ! command -v restic &> /dev/null; then
|
||||||
|
echo "Error: restic command not found." >&2
|
||||||
|
echo "Please install restic first: https://restic.net/" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BACKUP_MODE=""
|
||||||
|
REPO=""
|
||||||
|
UPLOAD_LIMIT=""
|
||||||
|
|
||||||
|
while getopts ":l:bu:h" opt; do
|
||||||
|
case ${opt} in
|
||||||
|
l)
|
||||||
|
BACKUP_MODE="local"
|
||||||
|
REPO="${OPTARG}"
|
||||||
|
;;
|
||||||
|
b)
|
||||||
|
BACKUP_MODE="b2"
|
||||||
|
if [[ ${OPTIND} -le $# && "${!OPTIND}" != -* ]]; then
|
||||||
|
REPO="b2:${!OPTIND}:"
|
||||||
|
OPTIND=$((OPTIND + 1))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
u)
|
||||||
|
UPLOAD_LIMIT="${OPTARG}"
|
||||||
|
;;
|
||||||
|
h)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
\?)
|
||||||
|
echo "Invalid option: -${OPTARG}" >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
:)
|
||||||
|
echo "Option -${OPTARG} requires an argument." >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- Pre-run checks ---
|
||||||
|
if [[ -z "${BACKUP_MODE}" ]]; then
|
||||||
|
echo "Error: You must specify a backup mode (-l or -b)." >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${BACKUP_MODE}" == "b2" ]]; then
|
||||||
|
if [[ -f "${HOME}/.resticb2" ]] ; then
|
||||||
|
. "${HOME}/.resticb2"
|
||||||
|
fi
|
||||||
|
export B2_ACCOUNT_ID
|
||||||
|
export B2_ACCOUNT_KEY
|
||||||
|
export B2_BUCKET_NAME
|
||||||
|
if [[ -z "${B2_ACCOUNT_ID:-}" || -z "${B2_ACCOUNT_KEY:-}" ]]; then
|
||||||
|
echo "Error: For Backblaze B2 backups, you must set the B2_ACCOUNT_ID and B2_ACCOUNT_KEY environment variables." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${REPO:-}" ]]; then
|
||||||
|
if [[ -n "${B2_BUCKET_NAME:-}" ]]; then
|
||||||
|
REPO="b2:${B2_BUCKET_NAME}:"
|
||||||
|
else
|
||||||
|
echo "Error: Backup mode is B2 but no bucket name was provided and the B2_BUCKET_NAME environment variable is not set." >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
KEYCHAIN_ENTRY_NAME="restic_repo_password"
|
||||||
|
if security find-generic-password -a "$(whoami)" -s "${KEYCHAIN_ENTRY_NAME}" >/dev/null 2>&1 ; then
|
||||||
|
export RESTIC_PASSWORD_COMMAND="security find-generic-password -a \"$(whoami)\" -s \"${KEYCHAIN_ENTRY_NAME}\" -w"
|
||||||
|
# Source file?
|
||||||
|
elif [[ -f "${HOME}/.resticpass" ]] ; then
|
||||||
|
export RESTIC_PASSWORD_FILE="${HOME}/.resticpass"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If the repository does not exist, initialize it.
|
||||||
|
# The user will be prompted for a password, which will be required for all
|
||||||
|
# future interactions with the repository.
|
||||||
|
if ! restic -r "${REPO}" snapshots &> /dev/null; then
|
||||||
|
echo "Restic repository not found or not accessible. Initializing..."
|
||||||
|
restic init -r "${REPO}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# --- Run Backup ---
|
||||||
|
echo "Starting restic backup..."
|
||||||
|
echo "Source: ${SOURCE_DIR}"
|
||||||
|
echo "Repository: ${REPO}"
|
||||||
|
|
||||||
|
BACKUP_CMD="restic backup \
|
||||||
|
--verbose \
|
||||||
|
--repo \"${REPO}\" \
|
||||||
|
--exclude-file \"${EXCLUDE_FILE}\" \
|
||||||
|
--one-file-system \
|
||||||
|
--tag \"macbook-backup\""
|
||||||
|
|
||||||
|
if [[ -n "${UPLOAD_LIMIT}" ]]; then
|
||||||
|
BACKUP_CMD="${BACKUP_CMD} --limit-upload ${UPLOAD_LIMIT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BACKUP_CMD="${BACKUP_CMD} \"${SOURCE_DIR}\""
|
||||||
|
|
||||||
|
eval "${BACKUP_CMD}"
|
||||||
|
|
||||||
|
echo "Backup complete."
|
||||||
|
|
||||||
|
# --- Prune old snapshots (optional, but recommended) ---
|
||||||
|
# Keeps the last 7 daily, 4 weekly, and 6 monthly snapshots.
|
||||||
|
echo "Pruning old snapshots..."
|
||||||
|
restic forget \
|
||||||
|
--repo "${REPO}" \
|
||||||
|
--keep-daily 7 \
|
||||||
|
--keep-weekly 4 \
|
||||||
|
--keep-monthly 6 \
|
||||||
|
--prune
|
||||||
|
|
||||||
|
echo "Pruning complete."
|
||||||
|
echo "Restic backup script finished."
|
||||||
37
bin/restic/scar
Executable file
37
bin/restic/scar
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ue
|
||||||
|
|
||||||
|
export RESTIC_DEFAULT_BE="google"
|
||||||
|
export RESTIC_PASSWORD_FILE=${HOME}/.restic-password
|
||||||
|
|
||||||
|
case "${RESTIC_BACKEND:=${RESTIC_DEFAULT_BE}}" in
|
||||||
|
google)
|
||||||
|
export GOOGLE_PROJECT_ID=systemoverlord.com:systemoverlord
|
||||||
|
export GOOGLE_APPLICATION_CREDENTIALS=${HOME}/.config/boto/restic-creds.json
|
||||||
|
export RESTIC_REPOSITORY="gs:systemoverlord-backups-scar-2:/"
|
||||||
|
;;
|
||||||
|
b2)
|
||||||
|
. "${HOME}/.restic-backups-scar-creds"
|
||||||
|
export AWS_ACCESS_KEY_ID
|
||||||
|
export AWS_SECRET_ACCESS_KEY
|
||||||
|
export RESTIC_REPOSITORY="s3:s3.us-west-004.backblazeb2.com/systemoverlord-backups-scar"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown restic backend $RESTIC_BACKEND" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
cd "${HOME}"
|
||||||
|
|
||||||
|
if [ -z "${1}" ] ; then
|
||||||
|
|
||||||
|
restic backup \
|
||||||
|
--files-from "${HOME}/.restic-backup" \
|
||||||
|
--limit-upload 5000 \
|
||||||
|
--limit-download 10000
|
||||||
|
|
||||||
|
else
|
||||||
|
restic "$@"
|
||||||
|
fi
|
||||||
107
bin/screenshot.sh
Executable file
107
bin/screenshot.sh
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Screenshot tool to try a few different tools
|
||||||
|
|
||||||
|
set -ue
|
||||||
|
|
||||||
|
TOOLS="flameshot scrot"
|
||||||
|
if [ "$(uname)" = "Darwin" ]; then
|
||||||
|
TOOLS="screencapture ${TOOLS}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCREENDIR=${SCREENDIR:-${HOME}/Pictures/Screenshots}
|
||||||
|
SCROT_FORMAT="%F-%T.png"
|
||||||
|
# Filename for screencapture
|
||||||
|
FILE_NAME=$(date "+%Y-%m-%d-%H%M%S.png")
|
||||||
|
|
||||||
|
function default_screenshot_command {
|
||||||
|
for tool in ${TOOLS} ; do
|
||||||
|
if which "${tool}" >/dev/null 2>&1 ; then
|
||||||
|
echo "${tool}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
TOOL=${SHOT:-$(default_screenshot_command)}
|
||||||
|
CMD=${1:-region}
|
||||||
|
|
||||||
|
function flameshot_gui_capture {
|
||||||
|
flameshot gui -p "${SCREENDIR}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function flameshot_full_capture {
|
||||||
|
flameshot full -p "${SCREENDIR}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrot_region_capture {
|
||||||
|
scrot -s "${SCREENDIR}/${SCROT_FORMAT}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrot_window_capture {
|
||||||
|
scrot -u "${SCREENDIR}/${SCROT_FORMAT}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrot_full_capture {
|
||||||
|
scrot "${SCREENDIR}/${SCROT_FORMAT}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function mac_capture {
|
||||||
|
local mode="${1}"
|
||||||
|
local target="${SCREENDIR}/${FILE_NAME}"
|
||||||
|
case "${mode}" in
|
||||||
|
region)
|
||||||
|
screencapture -i "${target}"
|
||||||
|
;;
|
||||||
|
window)
|
||||||
|
screencapture -i -w "${target}"
|
||||||
|
;;
|
||||||
|
full)
|
||||||
|
screencapture "${target}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${CMD}" in
|
||||||
|
region|window|full)
|
||||||
|
mkdir -p "${SCREENDIR}"
|
||||||
|
case "${TOOL}" in
|
||||||
|
screencapture)
|
||||||
|
mac_capture "${CMD}"
|
||||||
|
;;
|
||||||
|
flameshot)
|
||||||
|
case "${CMD}" in
|
||||||
|
region|window)
|
||||||
|
flameshot_gui_capture
|
||||||
|
;;
|
||||||
|
full)
|
||||||
|
flameshot_full_capture
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
scrot)
|
||||||
|
case "${CMD}" in
|
||||||
|
region)
|
||||||
|
scrot_region_capture
|
||||||
|
;;
|
||||||
|
window)
|
||||||
|
scrot_window_capture
|
||||||
|
;;
|
||||||
|
full)
|
||||||
|
scrot_full_capture
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Error: Unknown or unsupported tool '${TOOL}'" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 [region|window|full]" >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
33
bin/setup/nerdfonts.sh
Executable file
33
bin/setup/nerdfonts.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ue
|
||||||
|
|
||||||
|
VER="v3.4.0"
|
||||||
|
|
||||||
|
FONTS=(
|
||||||
|
https://github.com/ryanoasis/nerd-fonts/releases/download/${VER}/DejaVuSansMono.zip
|
||||||
|
https://github.com/ryanoasis/nerd-fonts/releases/download/${VER}/FiraCode.zip
|
||||||
|
https://github.com/ryanoasis/nerd-fonts/releases/download/${VER}/FiraMono.zip
|
||||||
|
https://github.com/ryanoasis/nerd-fonts/releases/download/${VER}/Hack.zip
|
||||||
|
https://github.com/ryanoasis/nerd-fonts/releases/download/${VER}/Inconsolata.zip
|
||||||
|
https://github.com/ryanoasis/nerd-fonts/releases/download/${VER}/OpenDyslexic.zip
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "$(uname)" = "Darwin" ]; then
|
||||||
|
FPATH="${HOME}/Library/Fonts"
|
||||||
|
else
|
||||||
|
FPATH="${HOME}/.local/share/fonts/nerdfonts"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "${FPATH}"
|
||||||
|
cd "${FPATH}"
|
||||||
|
|
||||||
|
for f in "${FONTS[@]}"; do
|
||||||
|
BN=$(basename "$f")
|
||||||
|
wget -O "${FPATH}/${BN}" "$f"
|
||||||
|
unzip -o -d "${FPATH}" "${FPATH}/${BN}"
|
||||||
|
done
|
||||||
|
|
||||||
|
if command -v fc-cache >/dev/null 2>&1; then
|
||||||
|
fc-cache -v
|
||||||
|
fi
|
||||||
164
bin/ssh-sign
Executable file
164
bin/ssh-sign
Executable file
@@ -0,0 +1,164 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# A robust wrapper for ssh-keygen to sign and verify files.
|
||||||
|
|
||||||
|
# --- Color Codes for Output ---
|
||||||
|
COLOR_RED='\033[0;31m'
|
||||||
|
COLOR_GREEN='\033[0;32m'
|
||||||
|
COLOR_NONE='\033[0m' # No Color
|
||||||
|
|
||||||
|
# --- Default values ---
|
||||||
|
DEFAULT_SIGNING_KEY="$HOME/.ssh/id_signing"
|
||||||
|
DEFAULT_ALLOWED_SIGNERS="$HOME/.ssh/allowed_signers"
|
||||||
|
DEFAULT_IDENTITY="david@systemoverlord.com"
|
||||||
|
DEFAULT_NAMESPACE="file"
|
||||||
|
|
||||||
|
# --- Usage Message ---
|
||||||
|
usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: $(basename "$0") <sign|verify> [OPTIONS] [FILE]
|
||||||
|
|
||||||
|
A wrapper for 'ssh-keygen -Y' to simplify file signing and verification.
|
||||||
|
|
||||||
|
COMMANDS:
|
||||||
|
sign Sign a file. The path to the file to be signed is provided as a positional argument.
|
||||||
|
verify Verify a signature. The original file content is read from stdin.
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-f <file> For 'sign': Path to the private key for signing.
|
||||||
|
Defaults to '$DEFAULT_SIGNING_KEY' if it exists.
|
||||||
|
For 'verify': Path to the allowed_signers file.
|
||||||
|
Defaults to '$DEFAULT_ALLOWED_SIGNERS'.
|
||||||
|
-n <namespace> Signature namespace.
|
||||||
|
Defaults to '$DEFAULT_NAMESPACE'.
|
||||||
|
-I <identity> For 'verify': The identity to check the signature against.
|
||||||
|
Defaults to '$DEFAULT_IDENTITY'.
|
||||||
|
-s <sig_file> For 'verify': Path to the signature file to verify (e.g., file.sig). REQUIRED for verify.
|
||||||
|
-h, --help Show this help message.
|
||||||
|
|
||||||
|
EXAMPLE USAGE:
|
||||||
|
# Sign a file using the default key
|
||||||
|
$(basename "$0") sign release.tar.gz
|
||||||
|
|
||||||
|
# Sign a file with a specific key
|
||||||
|
$(basename "$0") sign -f ~/.ssh/id_ed25519_my_signing_key release.tar.gz
|
||||||
|
|
||||||
|
# Verify a signature using default allowed_signers and identity
|
||||||
|
cat release.tar.gz | $(basename "$0") verify -s release.tar.gz.sig
|
||||||
|
|
||||||
|
# Verify a signature with a specific allowed_signers file and identity
|
||||||
|
cat release.tar.gz | $(basename "$0") verify -f ./my_signers -I other@example.com -s release.tar.gz.sig
|
||||||
|
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Helper for error messages ---
|
||||||
|
error() {
|
||||||
|
echo -e "${COLOR_RED}Error: $1${COLOR_NONE}" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Main Script Logic ---
|
||||||
|
|
||||||
|
if [[ "$1" != "sign" && "$1" != "verify" ]]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
SUBCOMMAND=$1
|
||||||
|
shift # Consume the subcommand
|
||||||
|
|
||||||
|
# --- Argument Parsing and Validation ---
|
||||||
|
|
||||||
|
# Separate arguments from the file to be signed
|
||||||
|
declare -a remaining_args
|
||||||
|
file_to_sign=""
|
||||||
|
while [[ "$#" -gt 0 ]]; do
|
||||||
|
# If we see a non-flag argument, assume it's the file to sign.
|
||||||
|
# This works because the file to sign is the only positional argument.
|
||||||
|
if [[ "$1" != -* ]] && [[ -z "$file_to_sign" ]]; then
|
||||||
|
file_to_sign="$1"
|
||||||
|
else
|
||||||
|
remaining_args+=("$1")
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- Build command based on subcommand ---
|
||||||
|
declare -a CMD_ARGS
|
||||||
|
CMD_ARGS=("ssh-keygen" "-Y" "$SUBCOMMAND")
|
||||||
|
|
||||||
|
# Append all the flag-based arguments (-f, -n, -I, -s)
|
||||||
|
CMD_ARGS+=("${remaining_args[@]}")
|
||||||
|
|
||||||
|
# Scan for provided flags to handle defaults correctly
|
||||||
|
f_provided=false
|
||||||
|
n_provided=false
|
||||||
|
I_provided=false
|
||||||
|
s_provided=false
|
||||||
|
for arg in "${remaining_args[@]}"; do
|
||||||
|
[[ "$arg" == "-f" ]] && f_provided=true
|
||||||
|
[[ "$arg" == "-n" ]] && n_provided=true
|
||||||
|
[[ "$arg" == "-I" ]] && I_provided=true
|
||||||
|
[[ "$arg" == "-s" ]] && s_provided=true
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$SUBCOMMAND" == "sign" ]]; then
|
||||||
|
if [[ -z "$file_to_sign" ]]; then
|
||||||
|
error "Path to file to be signed is required for 'sign' command."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set default signing key if -f was not provided
|
||||||
|
if ! $f_provided; then
|
||||||
|
if [[ ! -f "$DEFAULT_SIGNING_KEY" ]]; then
|
||||||
|
error "Default signing key not found at '$DEFAULT_SIGNING_KEY'. Specify one with -f."
|
||||||
|
fi
|
||||||
|
CMD_ARGS+=("-f" "$DEFAULT_SIGNING_KEY")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set default namespace if -n was not provided
|
||||||
|
if ! $n_provided; then
|
||||||
|
CMD_ARGS+=("-n" "$DEFAULT_NAMESPACE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The file to sign MUST be the last argument for ssh-keygen
|
||||||
|
CMD_ARGS+=("$file_to_sign")
|
||||||
|
|
||||||
|
elif [[ "$SUBCOMMAND" == "verify" ]]; then
|
||||||
|
if [[ -n "$file_to_sign" ]]; then
|
||||||
|
error "The 'verify' command reads from stdin and does not accept a positional file argument. Found '$file_to_sign'."
|
||||||
|
fi
|
||||||
|
if ! $s_provided; then
|
||||||
|
error "Signature file must be provided with -s for 'verify' command."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set default allowed_signers if -f was not provided
|
||||||
|
if ! $f_provided; then
|
||||||
|
if [[ ! -f "$DEFAULT_ALLOWED_SIGNERS" ]]; then
|
||||||
|
error "Default allowed signers file not found at '$DEFAULT_ALLOWED_SIGNERS'. Specify one with -f."
|
||||||
|
fi
|
||||||
|
CMD_ARGS+=("-f" "$DEFAULT_ALLOWED_SIGNERS")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set default identity if -I was not provided
|
||||||
|
if ! $I_provided; then
|
||||||
|
CMD_ARGS+=("-I" "$DEFAULT_IDENTITY")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set default namespace if -n was not provided
|
||||||
|
if ! $n_provided; then
|
||||||
|
CMD_ARGS+=("-n" "$DEFAULT_NAMESPACE")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Execute and Report ---
|
||||||
|
|
||||||
|
# We capture the output and stderr to show it to the user
|
||||||
|
if output=$("${CMD_ARGS[@]}" 2>&1); then
|
||||||
|
echo -e "${COLOR_GREEN}Success:${COLOR_NONE}"
|
||||||
|
echo "$output"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo -e "${COLOR_RED}Command Failed:${COLOR_NONE}"
|
||||||
|
echo "$output" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
310
bin/update-authorized-keys
Executable file
310
bin/update-authorized-keys
Executable file
@@ -0,0 +1,310 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# update-authorized-keys - Manage ~/.ssh/authorized_keys from multiple sources
|
||||||
|
#
|
||||||
|
# BEHAVIOR:
|
||||||
|
# 1. Collects SSH public keys from one or more source directories (default: ~/.ssh/authorized_keys.d).
|
||||||
|
# 2. Skips empty files and files symlinked to /dev/null (masking).
|
||||||
|
# 3. Deterministically concatenates keys into a "managed block" wrapped in markers:
|
||||||
|
# # BEGIN UPDATE-AUTHORIZED-KEYS
|
||||||
|
# # END UPDATE-AUTHORIZED-KEYS
|
||||||
|
# 4. Deduplicates managed keys: if the same key (including options) is found in multiple files,
|
||||||
|
# it is included once with a comment listing all source filenames.
|
||||||
|
# 5. Preserves "manual" keys found in the target file outside the markers.
|
||||||
|
# 6. Removes manual keys that exactly match a managed key (options + key data).
|
||||||
|
# 7. Validates every proposed key individually using 'ssh-keygen -l -f'.
|
||||||
|
# 8. Optionally validates the whole file with 'authorized-keys-test' if available.
|
||||||
|
# 9. Displays a unified diff and prompts for confirmation before atomic replacement.
|
||||||
|
# 10. Supports a --dry-run mode and a --self-test mode for verifying logic.
|
||||||
|
|
||||||
|
set -o nounset
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
CLEANUP_FILES=()
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "${CLEANUP_FILES[@]}"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
DEFAULT_DIR="${HOME}/.ssh/authorized_keys.d"
|
||||||
|
DEFAULT_TARGET="${HOME}/.ssh/authorized_keys"
|
||||||
|
BEGIN_MARKER="# BEGIN UPDATE-AUTHORIZED-KEYS"
|
||||||
|
END_MARKER="# END UPDATE-AUTHORIZED-KEYS"
|
||||||
|
|
||||||
|
# State
|
||||||
|
SOURCE_DIRS=()
|
||||||
|
TARGET_FILE="${DEFAULT_TARGET}"
|
||||||
|
DRY_RUN=0
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename "$0") [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--dir DIR Primary directory for managed keys (default: ${DEFAULT_DIR})
|
||||||
|
--extra-dir DIR Additional directory to scan for keys (can be repeated)
|
||||||
|
--target FILE Target authorized_keys file (default: ${DEFAULT_TARGET})
|
||||||
|
--dry-run Show changes and validate without modifying the target
|
||||||
|
--self-test Run internal suite of tests to verify script logic
|
||||||
|
--help Show this help message
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
run_self_test() {
|
||||||
|
echo "Running self-test..."
|
||||||
|
local test_root=$(mktemp -d)
|
||||||
|
CLEANUP_FILES+=("${test_root}")
|
||||||
|
|
||||||
|
local d1="${test_root}/d1"
|
||||||
|
local d2="${test_root}/d2"
|
||||||
|
local target="${test_root}/target"
|
||||||
|
mkdir -p "${d1}" "${d2}"
|
||||||
|
|
||||||
|
local key1="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ8XoR7N7X5XoR7N7X5XoR7N7X5XoR7N7X5XoR7N7X5X key1"
|
||||||
|
local key2="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL9YpS8O8Y6YpS8O8Y6YpS8O8Y6YpS8O8Y6YpS8O8Y6Y key2"
|
||||||
|
local key_man="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM0ZqT9P9Z7ZqT9P9Z7ZqT9P9Z7ZqT9P9Z7ZqT9P9Z7Z manual"
|
||||||
|
local long_opt="environment=\"VAR=VERY_LONG_VALUE_THAT_EXCEEDS_TWENTY_CHARS\""
|
||||||
|
|
||||||
|
echo "${key1}" > "${d1}/k1"
|
||||||
|
echo "${key1}" > "${d2}/k1_dup"
|
||||||
|
echo "${key2}" > "${d2}/k2"
|
||||||
|
echo "${long_opt} ${key1}" > "${d1}/k1_long"
|
||||||
|
echo "${long_opt} ${key2}" > "${d1}/k2_long"
|
||||||
|
ln -s /dev/null "${d1}/masked"
|
||||||
|
|
||||||
|
cat <<EOF > "${target}"
|
||||||
|
${key_man}
|
||||||
|
${key1} # This should be removed as it's now managed
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Executing script in test mode..."
|
||||||
|
# Pipe "y" to handle the TTY check if we are not in a TTY during test
|
||||||
|
echo "y" | "$0" --dir "${d1}" --extra-dir "${d2}" --target "${target}" > /dev/null
|
||||||
|
|
||||||
|
local content=$(cat "${target}")
|
||||||
|
|
||||||
|
echo -n "Check markers... "
|
||||||
|
if [[ "${content}" == *"${BEGIN_MARKER}"* && "${content}" == *"${END_MARKER}"* ]]; then echo "OK"; else echo "FAIL"; exit 1; fi
|
||||||
|
|
||||||
|
echo -n "Check managed deduplication... "
|
||||||
|
if grep -q "Source: k1, k1_dup" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
|
||||||
|
|
||||||
|
echo -n "Check long option deduplication (should NOT deduplicate different keys)... "
|
||||||
|
if grep -q "k1_long" "${target}" && grep -q "k2_long" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
|
||||||
|
|
||||||
|
echo -n "Check manual key preservation... "
|
||||||
|
if grep -q "manual" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
|
||||||
|
|
||||||
|
echo -n "Check manual key filtering... "
|
||||||
|
local manual_count=$(grep -c "${key1}" "${target}")
|
||||||
|
# key1 appears twice in managed block (once plain, once with long opt)
|
||||||
|
# and it was in manual block. The manual one should be removed.
|
||||||
|
# So we expect 2 occurrences in the final file (both in managed block).
|
||||||
|
if [[ ${manual_count} -eq 2 ]]; then echo "OK"; else echo "FAIL (Found ${manual_count} occurrences, expected 2)"; exit 1; fi
|
||||||
|
|
||||||
|
echo -n "Check masking... "
|
||||||
|
if ! grep -q "masked" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
|
||||||
|
|
||||||
|
echo "Self-test passed successfully!"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--dir)
|
||||||
|
[[ -z "${2:-}" ]] && { echo "Error: --dir requires an argument" >&2; exit 1; }
|
||||||
|
SOURCE_DIRS+=("$2"); shift 2 ;;
|
||||||
|
--extra-dir)
|
||||||
|
[[ -z "${2:-}" ]] && { echo "Error: --extra-dir requires an argument" >&2; exit 1; }
|
||||||
|
SOURCE_DIRS+=("$2"); shift 2 ;;
|
||||||
|
--target)
|
||||||
|
[[ -z "${2:-}" ]] && { echo "Error: --target requires an argument" >&2; exit 1; }
|
||||||
|
TARGET_FILE="$2"; shift 2 ;;
|
||||||
|
--dry-run) DRY_RUN=1; shift ;;
|
||||||
|
--self-test) run_self_test ;;
|
||||||
|
--help) usage; exit 0 ;;
|
||||||
|
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#SOURCE_DIRS[@]} -eq 0 ]]; then
|
||||||
|
SOURCE_DIRS+=("${DEFAULT_DIR}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "${TARGET_FILE}")"
|
||||||
|
TMP_FILE=$(mktemp)
|
||||||
|
CLEANUP_FILES+=("${TMP_FILE}")
|
||||||
|
|
||||||
|
collect_keys() {
|
||||||
|
local dirs=("${@}")
|
||||||
|
for dir in "${dirs[@]}"; do
|
||||||
|
if [[ ! -d "${dir}" ]]; then continue; fi
|
||||||
|
# Use a glob to avoid parsing ls
|
||||||
|
for file in "${dir}"/*; do
|
||||||
|
[[ ! -e "${file}" ]] && continue
|
||||||
|
[[ ! -f "${file}" || ! -s "${file}" ]] && continue
|
||||||
|
if [[ -L "${file}" && "$(readlink "${file}")" == "/dev/null" ]]; then continue; fi
|
||||||
|
while read -r line; do
|
||||||
|
[[ -z "${line}" || "${line}" =~ ^[[:space:]]*# ]] && continue
|
||||||
|
# Use a specific delimiter that is unlikely to be in the key or filename
|
||||||
|
# If using tabs, ensure we only split on the first one in AWK
|
||||||
|
printf "%s\t%s\n" "$(basename "${file}")" "${line}"
|
||||||
|
done < "${file}"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use a HEREDOC for the complex AWK script to avoid shell interpolation issues
|
||||||
|
MANAGED_BLOCK=$(collect_keys "${SOURCE_DIRS[@]}" | awk -F'\t' '
|
||||||
|
{
|
||||||
|
# Splitting on the first tab manually to be robust
|
||||||
|
tab_idx = index($0, "\t")
|
||||||
|
source = substr($0, 1, tab_idx - 1)
|
||||||
|
full_line = substr($0, tab_idx + 1)
|
||||||
|
|
||||||
|
# Signature detection: all options + key type + key data
|
||||||
|
# (Excludes the comment at the end)
|
||||||
|
n = split(full_line, parts, " ")
|
||||||
|
sig = ""
|
||||||
|
for (i=1; i<=n; i++) {
|
||||||
|
sig = (sig == "" ? parts[i] : sig " " parts[i])
|
||||||
|
# A key line is [options] <type> <base64> [comment]
|
||||||
|
# We stop after the base64 part. Key types start with known prefixes.
|
||||||
|
if (parts[i] ~ /^(ssh-|ecdsa-|sk-)/ && i < n) {
|
||||||
|
sig = sig " " parts[i+1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Fallback if no key type found (should not happen with valid keys)
|
||||||
|
if (sig == "") sig = full_line
|
||||||
|
|
||||||
|
if (!(sig in keys)) {
|
||||||
|
keys[sig] = full_line
|
||||||
|
order[++count] = sig
|
||||||
|
}
|
||||||
|
sources[sig] = (sources[sig] ? sources[sig] ", " : "") source
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
for (i=1; i<=count; i++) {
|
||||||
|
sig = order[i]
|
||||||
|
print "# Source: " sources[sig]
|
||||||
|
print keys[sig]
|
||||||
|
}
|
||||||
|
}')
|
||||||
|
|
||||||
|
MANUAL_KEYS=""
|
||||||
|
if [[ -f "${TARGET_FILE}" ]]; then
|
||||||
|
MANUAL_KEYS=$(awk -v begin="${BEGIN_MARKER}" -v end="${END_MARKER}" '
|
||||||
|
BEGIN { inside=0 }
|
||||||
|
$0 == begin { inside=1; next }
|
||||||
|
$0 == end { inside=0; next }
|
||||||
|
!inside { print $0 }
|
||||||
|
' "${TARGET_FILE}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
MANAGED_SIGS_TMP=$(mktemp)
|
||||||
|
echo "${MANAGED_BLOCK}" | awk '/^[^#]/ {
|
||||||
|
n = split($0, parts, " ")
|
||||||
|
sig = ""
|
||||||
|
for (i=1; i<=n; i++) {
|
||||||
|
sig = (sig == "" ? parts[i] : sig " " parts[i])
|
||||||
|
if (parts[i] ~ /^(ssh-|ecdsa-|sk-)/ && i < n) {
|
||||||
|
sig = sig " " parts[i+1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sig != "") print sig
|
||||||
|
}' > "${MANAGED_SIGS_TMP}"
|
||||||
|
|
||||||
|
FINAL_MANUAL_KEYS=$(echo "${MANUAL_KEYS}" | awk -v sigs_file="${MANAGED_SIGS_TMP}" '
|
||||||
|
BEGIN {
|
||||||
|
while ((getline line < sigs_file) > 0) {
|
||||||
|
managed[line] = 1
|
||||||
|
}
|
||||||
|
close(sigs_file)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if ($0 ~ /^[[:space:]]*$/ || $0 ~ /^[[:space:]]*#/) {
|
||||||
|
print $0
|
||||||
|
next
|
||||||
|
}
|
||||||
|
n = split($0, parts, " ")
|
||||||
|
sig = ""
|
||||||
|
for (i=1; i<=n; i++) {
|
||||||
|
sig = (sig == "" ? parts[i] : sig " " parts[i])
|
||||||
|
if (parts[i] ~ /^(ssh-|ecdsa-|sk-)/ && i < n) {
|
||||||
|
sig = sig " " parts[i+1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(sig in managed)) {
|
||||||
|
print $0
|
||||||
|
}
|
||||||
|
}')
|
||||||
|
rm -f "${MANAGED_SIGS_TMP}"
|
||||||
|
|
||||||
|
{
|
||||||
|
if [[ -n "${MANAGED_BLOCK}" ]]; then
|
||||||
|
echo "${BEGIN_MARKER}"
|
||||||
|
echo "${MANAGED_BLOCK}"
|
||||||
|
echo "${END_MARKER}"
|
||||||
|
fi
|
||||||
|
echo "${FINAL_MANUAL_KEYS}"
|
||||||
|
} > "${TMP_FILE}"
|
||||||
|
|
||||||
|
echo "Validating proposed changes..."
|
||||||
|
VALID=1
|
||||||
|
while read -r line; do
|
||||||
|
[[ -z "${line}" || "${line}" =~ ^[[:space:]]*# ]] && continue
|
||||||
|
if ! echo "${line}" | ssh-keygen -l -f - >/dev/null 2>&1; then
|
||||||
|
echo "ERROR: Invalid SSH key detected: ${line}" >&2
|
||||||
|
VALID=0
|
||||||
|
fi
|
||||||
|
done < "${TMP_FILE}"
|
||||||
|
|
||||||
|
if command -v authorized-keys-test >/dev/null 2>&1; then
|
||||||
|
if ! authorized-keys-test "${TMP_FILE}"; then
|
||||||
|
echo "ERROR: Proposed file failed authorized-keys-test." >&2
|
||||||
|
VALID=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${VALID} -eq 0 ]]; then
|
||||||
|
echo "Validation failed. Aborting." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "${TARGET_FILE}" ]]; then
|
||||||
|
diff -u "${TARGET_FILE}" "${TMP_FILE}" || true
|
||||||
|
else
|
||||||
|
echo "Target file does not exist. Proposed content:"
|
||||||
|
cat "${TMP_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${DRY_RUN} -eq 1 ]]; then
|
||||||
|
echo "Dry run complete. No changes made."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -t 0 ]]; then
|
||||||
|
echo -n "Apply these changes to ${TARGET_FILE}? [y/N] "
|
||||||
|
read -r response
|
||||||
|
elif [[ ! -t 0 ]]; then
|
||||||
|
# Read from pipe or file if provided
|
||||||
|
if ! read -r response; then
|
||||||
|
echo "Non-interactive shell detected and no input provided. Aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${response}" =~ ^([yY][eE][sS]|[yY])$ ]]; then
|
||||||
|
chmod 0600 "${TMP_FILE}"
|
||||||
|
mv "${TMP_FILE}" "${TARGET_FILE}"
|
||||||
|
echo "Changes applied successfully."
|
||||||
|
else
|
||||||
|
echo "Aborted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
7
bin/update_skel
Executable file
7
bin/update_skel
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
SKEL_DIR=$(dirname -- "$(readlink -f -- "$HOME/.profile")")
|
||||||
|
cd -- "$SKEL_DIR"
|
||||||
|
cd -- "$(git rev-parse --show-toplevel)"
|
||||||
|
git pull
|
||||||
|
./install.sh
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"background-color": "rgba(0, 43, 54, 1)",
|
|
||||||
"cursor-color": "rgba(238, 232, 213, 0.5)",
|
|
||||||
"color-palette-overrides": {
|
|
||||||
"0": "#073642",
|
|
||||||
"1": "#dc322f",
|
|
||||||
"2": "#859900",
|
|
||||||
"3": "#b58900",
|
|
||||||
"4": "#268bd2",
|
|
||||||
"5": "#d33682",
|
|
||||||
"6": "#2aa198",
|
|
||||||
"7": "#eee8d5",
|
|
||||||
"8": "#002b36",
|
|
||||||
"9": "#cb4b16",
|
|
||||||
"10": "#586e75",
|
|
||||||
"11": "#657b83",
|
|
||||||
"12": "#839496",
|
|
||||||
"13": "#6c71c4",
|
|
||||||
"14": "#93a1a1",
|
|
||||||
"15": "#fdf6e3"
|
|
||||||
},
|
|
||||||
"font-family": "\"Inconsolata\", \"DejaVu Sans Mono\", \"Noto Sans Mono\", \"Everson Mono\", FreeMono, Menlo, Terminal, monospace",
|
|
||||||
"font-size": "15",
|
|
||||||
"foreground-color": "rgba(238, 232, 213, 1)",
|
|
||||||
"user-css": "https://cdn.jsdelivr.net/gh/wernight/powerline-web-fonts@ba4426cb0c0b05eb6cb342c7719776a41e1f2114/PowerlineFonts.css"
|
|
||||||
}
|
|
||||||
69
clone.sh
Executable file
69
clone.sh
Executable file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ue
|
||||||
|
|
||||||
|
# --- Helper function to install git ---
|
||||||
|
install_git() {
|
||||||
|
echo "Git not found. Attempting to install..." >&2
|
||||||
|
case "$(uname)" in
|
||||||
|
Darwin)
|
||||||
|
if command -v brew >/dev/null 2>&1; then
|
||||||
|
echo "Using Homebrew to install git..." >&2
|
||||||
|
brew install git
|
||||||
|
else
|
||||||
|
echo "Error: Homebrew not found on your macOS system." >&2
|
||||||
|
echo "Please install Homebrew first by visiting https://brew.sh/ then run this script again." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
Linux)
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
echo "Using apt-get to install git..." >&2
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
sudo apt-get update && sudo apt-get install -y git
|
||||||
|
else
|
||||||
|
apt-get update && apt-get install -y git
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Error: This script requires 'apt-get' on Linux to install git." >&2
|
||||||
|
echo "Please install git using your system's package manager and run this script again." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Error: Unsupported operating system '$(uname)'." >&2
|
||||||
|
echo "Please install git manually and run this script again." >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Main script logic ---
|
||||||
|
installer_main() {
|
||||||
|
# 1. Check for git, and try to install it if it's missing.
|
||||||
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
|
install_git
|
||||||
|
# Final check after attempting installation
|
||||||
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
|
echo "ERROR: git installation failed or was not found." >&2
|
||||||
|
echo "Please install git manually and re-run this script." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Clone the repository if it doesn't exist
|
||||||
|
local dest="${HOME}/.skel"
|
||||||
|
if [ -d "${dest}" ]; then
|
||||||
|
echo "Repository already exists in ${dest}. Skipping clone." >&2
|
||||||
|
else
|
||||||
|
echo "Cloning repository..." >&2
|
||||||
|
git clone --depth 1 https://github.com/Matir/skel.git "${dest}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Run the main installer
|
||||||
|
echo "Running main installer..." >&2
|
||||||
|
"${dest}/install.sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
installer_main
|
||||||
|
|
||||||
10
conf/logid.cfg
Normal file
10
conf/logid.cfg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
devices: ({
|
||||||
|
name: "Wireless Mouse MX Master 3";
|
||||||
|
|
||||||
|
smartshift: {
|
||||||
|
on: true;
|
||||||
|
threshold: 30;
|
||||||
|
};
|
||||||
|
|
||||||
|
dpi: 1500;
|
||||||
|
});
|
||||||
16
darwin-env.plist
Normal file
16
darwin-env.plist
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Label</key>
|
||||||
|
<string>com.user.loadvars</string>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
<string>/bin/sh</string>
|
||||||
|
<string>-c</string>
|
||||||
|
<string>~/bin/darwin-env.sh || true</string>
|
||||||
|
</array>
|
||||||
|
<key>RunAtLoad</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
[media-keys]
|
|
||||||
screensaver=['<Primary><Alt>l', 'XF86ScreenSaver']
|
|
||||||
|
|
||||||
[wm]
|
|
||||||
move-to-workspace-1=['<Shift><Super>exclam']
|
|
||||||
move-to-workspace-2=['<Shift><Super>at']
|
|
||||||
move-to-workspace-3=['<Shift><Super>numbersign']
|
|
||||||
move-to-workspace-4=['<Shift><Super>dollar']
|
|
||||||
move-to-workspace-5=['<Shift><Super>percent']
|
|
||||||
move-to-workspace-6=['<Shift><Super>asciicircum']
|
|
||||||
move-to-workspace-7=['<Shift><Super>ampersand']
|
|
||||||
move-to-workspace-8=['<Shift><Super>asterisk']
|
|
||||||
move-to-workspace-9=['<Shift><Super>parenleft']
|
|
||||||
switch-to-workspace-4=['<Super>4']
|
|
||||||
switch-to-workspace-1=['<Super>1']
|
|
||||||
switch-to-workspace-10=['<Super>0']
|
|
||||||
switch-to-workspace-3=['<Super>3']
|
|
||||||
switch-to-workspace-8=['<Super>8']
|
|
||||||
switch-to-workspace-5=['<Super>5']
|
|
||||||
move-to-workspace-10=['<Shift><Super>parenright']
|
|
||||||
switch-to-workspace-2=['<Super>2']
|
|
||||||
switch-to-workspace-9=['<Super>9']
|
|
||||||
switch-to-workspace-6=['<Super>6']
|
|
||||||
switch-to-workspace-7=['<Super>7']
|
|
||||||
1
dotfile_overlays/README
Normal file
1
dotfile_overlays/README
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Each directory in this directory will be symlinked as dotfiles.
|
||||||
1
dotfiles/Xkbmap
Normal file
1
dotfiles/Xkbmap
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-option ctrl:nocaps -option compose:ralt
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
--follow
|
--follow
|
||||||
--pager=less -LMXRF
|
--pager=less -LMXRF
|
||||||
--smart-case
|
--smart-case
|
||||||
|
--ignore-dir=match:bazel-.*
|
||||||
|
|||||||
@@ -1,25 +1,5 @@
|
|||||||
# General aliases, should only be sourced in interactive shells
|
# General aliases, should only be sourced in interactive shells
|
||||||
|
# Try to keep in sync with ~/.config/fish/conf.d/aliases.fish
|
||||||
# Add an "alert" alias for long running commands. Use like so:
|
|
||||||
# sleep 10; alert
|
|
||||||
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
|
|
||||||
|
|
||||||
# Cryptsetup alias
|
|
||||||
alias luksFormat='cryptsetup luksFormat -s 512 -c aes-xts-plain64 -h sha256 -i 15000'
|
|
||||||
|
|
||||||
# Colors
|
|
||||||
if ls --version >/dev/null 2>&1 ; then
|
|
||||||
alias ls='ls --color=auto'
|
|
||||||
fi
|
|
||||||
if [ `uname` != 'Darwin' -a `uname` != 'NetBSD' -a `uname` != 'FreeBSD' -a `uname` != 'OpenBSD' ] ; then
|
|
||||||
# Should have a better way to check for GNU versions
|
|
||||||
alias grep='grep --color=auto'
|
|
||||||
alias egrep='egrep --color=auto'
|
|
||||||
alias fgrep='fgrep --color=auto'
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Easy upgrade
|
|
||||||
alias dist-upgrade="sudo sh -c 'apt-get update && apt-get -y dist-upgrade'"
|
|
||||||
|
|
||||||
# Timestamp in a machine-sortable form
|
# Timestamp in a machine-sortable form
|
||||||
alias tstamp="date '+%Y%m%d-%H%M%S'"
|
alias tstamp="date '+%Y%m%d-%H%M%S'"
|
||||||
@@ -30,11 +10,49 @@ alias mdcode="sed 's/^/ /'"
|
|||||||
# Intel format plz
|
# Intel format plz
|
||||||
alias objdump="command objdump -M intel"
|
alias objdump="command objdump -M intel"
|
||||||
|
|
||||||
# Useful directory utilities
|
# get git working directory
|
||||||
alias dircount="for d in * ; do find \$d -type d | wc -l | tr -d '\n' ; echo ' ' \$d ; done | sort -n"
|
alias gitroot="git rev-parse --show-toplevel"
|
||||||
|
|
||||||
# Drop caches for swap issues
|
# SSH without host key checking
|
||||||
alias drop_caches="echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
|
alias sshanon="ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no"
|
||||||
|
|
||||||
# dump acpi temperature
|
# Straight to ipython
|
||||||
alias temp='printf "%02.2f\n" "$(cat /sys/class/thermal/thermal_zone0/temp)e-3"'
|
alias ipy="ipython3 --no-banner"
|
||||||
|
|
||||||
|
# Skip the header on bc
|
||||||
|
alias bc="command bc -q"
|
||||||
|
|
||||||
|
# Clear the GPG agent
|
||||||
|
alias clear-gpg-agent="echo RELOADAGENT | gpg-connect-agent"
|
||||||
|
|
||||||
|
# Earthly ssh
|
||||||
|
alias earthly='earthly --ssh-auth-sock ""'
|
||||||
|
|
||||||
|
if [ "$(uname)" = "Linux" ]; then
|
||||||
|
# Cryptsetup alias
|
||||||
|
alias luksFormat='cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64'
|
||||||
|
|
||||||
|
# Drop caches for swap issues
|
||||||
|
alias drop_caches="echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
|
||||||
|
|
||||||
|
# dump acpi temperature
|
||||||
|
alias gettemp='printf "%02.2f\n" "$(cat /sys/class/thermal/thermal_zone0/temp)e-3"'
|
||||||
|
|
||||||
|
# Get a decently readable df
|
||||||
|
alias dfh="df -h -x tmpfs -x devtmpfs -x squashfs -x fuse -x efivarfs"
|
||||||
|
|
||||||
|
# Battery details
|
||||||
|
alias bat-details='upower -i $(upower -e | grep battery)'
|
||||||
|
|
||||||
|
# Nvidia refresh rate
|
||||||
|
alias nvidia-refresh-rate='nvidia-settings --display=:0 -q RefreshRate -t'
|
||||||
|
|
||||||
|
# to clipboard
|
||||||
|
alias toclip='xclip -selection clipboard'
|
||||||
|
elif [ "$(uname)" = "Darwin" ]; then
|
||||||
|
# Get a decently readable df
|
||||||
|
alias dfh="df -h"
|
||||||
|
|
||||||
|
# to clipboard
|
||||||
|
alias toclip='pbcopy'
|
||||||
|
fi
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Load env first
|
# Load env first
|
||||||
if [ -f $HOME/.env ] ; then source $HOME/.env ; fi
|
if [ -f $HOME/.shenv ] ; then source $HOME/.shenv ; fi
|
||||||
|
|
||||||
# History settings
|
# History settings
|
||||||
HISTCONTROL=ignoredups:ignorespace
|
HISTCONTROL=ignoredups:ignorespace
|
||||||
|
|||||||
4
dotfiles/bxignore
Normal file
4
dotfiles/bxignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Credentials
|
||||||
|
.ssh
|
||||||
|
Passwords.kdbx
|
||||||
|
Passwords.kdbx.age
|
||||||
15
dotfiles/config/Code/User/settings.json
Normal file
15
dotfiles/config/Code/User/settings.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"editor.accessibilitySupport": "off",
|
||||||
|
"telemetry.telemetryLevel": "off",
|
||||||
|
"dotfiles.repository": "matir/skel",
|
||||||
|
"dotfiles.targetPath": "~/.skel",
|
||||||
|
"remote.defaultExtensionsIfInstalledLocally": [],
|
||||||
|
"mise.configureExtensionsAutomatically": true,
|
||||||
|
"kilo-code.debug": false,
|
||||||
|
"kilo-code.allowedCommands": [
|
||||||
|
"git log",
|
||||||
|
"git diff",
|
||||||
|
"git show"
|
||||||
|
],
|
||||||
|
"kilo-code.deniedCommands": []
|
||||||
|
}
|
||||||
5
dotfiles/config/direnv/lib/mise.sh
Normal file
5
dotfiles/config/direnv/lib/mise.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
if command -v mise ; then
|
||||||
|
use_mise() {
|
||||||
|
eval "$(mise direnv activate)"
|
||||||
|
}
|
||||||
|
fi
|
||||||
16
dotfiles/config/direnv/lib/python.sh
Normal file
16
dotfiles/config/direnv/lib/python.sh
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
layout_python() {
|
||||||
|
local DIR_NAME="$(basename $(pwd))"
|
||||||
|
VIRTUAL_ENV="${VIRTUAL_ENV:-$(pwd)/.venv/${DIR_NAME}}"
|
||||||
|
local PYBIN="$(command -v python 2>/dev/null || command -v python3 2>/dev/null)"
|
||||||
|
if [[ -z "${PYBIN}" ]]; then
|
||||||
|
log_error "No python found!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ ! -d $VIRTUAL_ENV ]]; then
|
||||||
|
log_status "No virtual environment exists. Executing \`${PYBIN} -m venv ${VIRTUAL_ENV}\`."
|
||||||
|
"${PYBIN}" -m venv "${VIRTUAL_ENV}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Activate the virtual environment
|
||||||
|
. $VIRTUAL_ENV/bin/activate
|
||||||
|
}
|
||||||
85
dotfiles/config/fish/conf.d/aliases.fish
Normal file
85
dotfiles/config/fish/conf.d/aliases.fish
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Timestamp in a machine-sortable form
|
||||||
|
alias tstamp "date '+%Y%m%d-%H%M%S'"
|
||||||
|
|
||||||
|
# Prepare code for markdown
|
||||||
|
alias mdcode "sed 's/^/ /'"
|
||||||
|
|
||||||
|
# Intel format plz
|
||||||
|
alias objdump "command objdump -M intel"
|
||||||
|
|
||||||
|
# get git working directory
|
||||||
|
alias gitroot "git rev-parse --show-toplevel"
|
||||||
|
|
||||||
|
# SSH without host key checking
|
||||||
|
alias sshanon "ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no"
|
||||||
|
|
||||||
|
# Straight to ipython
|
||||||
|
alias ipy "ipython3 --no-banner"
|
||||||
|
|
||||||
|
# Skip the header on bc
|
||||||
|
alias bc "command bc -q"
|
||||||
|
|
||||||
|
# Clear the GPG agent
|
||||||
|
alias clear-gpg-agent "echo RELOADAGENT | gpg-connect-agent"
|
||||||
|
|
||||||
|
# Earthly ssh
|
||||||
|
alias earthly 'earthly --ssh-auth-sock ""'
|
||||||
|
|
||||||
|
if test (uname) = "Linux"
|
||||||
|
# Cryptsetup alias
|
||||||
|
alias luksFormat 'cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64'
|
||||||
|
|
||||||
|
# Drop caches for swap issues
|
||||||
|
alias drop_caches "echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
|
||||||
|
|
||||||
|
# dump acpi temperature
|
||||||
|
alias gettemp 'printf "%02.2f\n" (cat /sys/class/thermal/thermal_zone0/temp)e-3'
|
||||||
|
|
||||||
|
# Get a decently readable df
|
||||||
|
alias dfh "df -h -x tmpfs -x devtmpfs -x squashfs -x fuse -x efivarfs"
|
||||||
|
|
||||||
|
# Battery details
|
||||||
|
alias bat-details 'upower -i (upower -e | grep battery)'
|
||||||
|
|
||||||
|
# Nvidia refresh rate
|
||||||
|
alias nvidia-refresh-rate 'nvidia-settings --display=:0 -q RefreshRate -t'
|
||||||
|
|
||||||
|
# to clipboard
|
||||||
|
alias toclip 'xclip -selection clipboard'
|
||||||
|
else if test (uname) = "Darwin"
|
||||||
|
# Get a decently readable df
|
||||||
|
alias dfh "df -h"
|
||||||
|
|
||||||
|
# to clipboard
|
||||||
|
alias toclip 'pbcopy'
|
||||||
|
end
|
||||||
|
|
||||||
|
# On some systems, bat is batcat
|
||||||
|
if not command -v bat >/dev/null 2>&1
|
||||||
|
if command -v batcat >/dev/null 2>&1
|
||||||
|
alias bat (command -v batcat)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# FFUF aliases
|
||||||
|
if command -v ffuf >/dev/null 2>&1
|
||||||
|
if test -d $HOME/tools/seclists
|
||||||
|
alias ffuf-files "ffuf -c -w $HOME/tools/seclists/Discovery/Web-Content/raft-large-files.txt"
|
||||||
|
alias ffuf-dirs "ffuf -c -w $HOME/tools/seclists/Discovery/Web-Content/raft-large-directories.txt"
|
||||||
|
alias ffuf-quick "ffuf -c -w $HOME/tools/seclists/Discovery/Web-Content/quickhits.txt"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if grep --help 2>/dev/null | grep -q 'color'
|
||||||
|
# Should have a better way to check for GNU versions
|
||||||
|
alias grep 'grep --color=auto'
|
||||||
|
alias egrep 'egrep --color=auto'
|
||||||
|
alias fgrep 'fgrep --color=auto'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Detect which `ls` flavor is in use and use the right flag for colors.
|
||||||
|
if ls --help 2>&1 | grep -q -- '--color'
|
||||||
|
alias ls 'ls --color=auto' # GNU `ls`
|
||||||
|
else if test (uname) = "Darwin"
|
||||||
|
alias ls 'ls -G' # macOS `ls`
|
||||||
|
end
|
||||||
14
dotfiles/config/fish/conf.d/fish_frozen_key_bindings.fish
Normal file
14
dotfiles/config/fish/conf.d/fish_frozen_key_bindings.fish
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# This file was created by fish when upgrading to version 4.3, to migrate
|
||||||
|
# the 'fish_key_bindings' variable from its old default scope (universal)
|
||||||
|
# to its new default scope (global). We recommend you delete this file
|
||||||
|
# and configure key bindings in ~/.config/fish/config.fish if needed.
|
||||||
|
|
||||||
|
# set --global fish_key_bindings fish_default_key_bindings
|
||||||
|
|
||||||
|
# Prior to version 4.3, fish shipped an event handler that runs
|
||||||
|
# `set --universal fish_key_bindings fish_default_key_bindings`
|
||||||
|
# whenever the fish_key_bindings variable is erased.
|
||||||
|
# This means that as long as any fish < 4.3 is still running on this system,
|
||||||
|
# we cannot complete the migration.
|
||||||
|
# As a workaround, erase the universal variable at every shell startup.
|
||||||
|
set --erase --universal fish_key_bindings
|
||||||
48
dotfiles/config/fish/conf.d/fish_frozen_theme.fish
Normal file
48
dotfiles/config/fish/conf.d/fish_frozen_theme.fish
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# This file was created by fish when upgrading to version 4.3, to migrate
|
||||||
|
# theme variables from universal to global scope.
|
||||||
|
# Don't edit this file, as it will be written by the web-config tool (`fish_config`).
|
||||||
|
# To customize your theme, delete this file and see
|
||||||
|
# help interactive#syntax-highlighting
|
||||||
|
# or
|
||||||
|
# man fish-interactive | less +/^SYNTAX.HIGHLIGHTING
|
||||||
|
# for appropriate commands to add to ~/.config/fish/config.fish instead.
|
||||||
|
# See also the release notes for fish 4.3.0 (run `help relnotes`).
|
||||||
|
|
||||||
|
set --global fish_color_autosuggestion 4D5566
|
||||||
|
set --global fish_color_cancel --reverse
|
||||||
|
set --global fish_color_command 39BAE6
|
||||||
|
set --global fish_color_comment 626A73
|
||||||
|
set --global fish_color_cwd 59C2FF
|
||||||
|
set --global fish_color_cwd_root red
|
||||||
|
set --global fish_color_end F29668
|
||||||
|
set --global fish_color_error FF3333
|
||||||
|
set --global fish_color_escape 95E6CB
|
||||||
|
set --global fish_color_history_current --bold
|
||||||
|
set --global fish_color_host normal
|
||||||
|
set --global fish_color_host_remote yellow
|
||||||
|
set --global fish_color_keyword 39BAE6
|
||||||
|
set --global fish_color_match F07178
|
||||||
|
set --global fish_color_normal B3B1AD
|
||||||
|
set --global fish_color_operator E6B450
|
||||||
|
set --global fish_color_option B3B1AD
|
||||||
|
set --global fish_color_param B3B1AD
|
||||||
|
set --global fish_color_quote C2D94C
|
||||||
|
set --global fish_color_redirection FFEE99
|
||||||
|
set --global fish_color_search_match --background=E6B450
|
||||||
|
set --global fish_color_selection --background=E6B450
|
||||||
|
set --global fish_color_status red
|
||||||
|
set --global fish_color_user brgreen
|
||||||
|
set --global fish_color_valid_path --underline
|
||||||
|
set --global fish_pager_color_background
|
||||||
|
set --global fish_pager_color_completion normal
|
||||||
|
set --global fish_pager_color_description B3A06D
|
||||||
|
set --global fish_pager_color_prefix normal --bold --underline
|
||||||
|
set --global fish_pager_color_progress brwhite --background=cyan
|
||||||
|
set --global fish_pager_color_secondary_background
|
||||||
|
set --global fish_pager_color_secondary_completion
|
||||||
|
set --global fish_pager_color_secondary_description
|
||||||
|
set --global fish_pager_color_secondary_prefix
|
||||||
|
set --global fish_pager_color_selected_background --background=E6B450
|
||||||
|
set --global fish_pager_color_selected_completion
|
||||||
|
set --global fish_pager_color_selected_description
|
||||||
|
set --global fish_pager_color_selected_prefix
|
||||||
95
dotfiles/config/fish/conf.d/gemini.fish
Normal file
95
dotfiles/config/fish/conf.d/gemini.fish
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Bridge to Gemini CLI Context Management (Zsh-backed)
|
||||||
|
# This allows using the Zsh implementation from Fish to avoid dual maintenance.
|
||||||
|
|
||||||
|
# Ensure the base context directory exists
|
||||||
|
if set -q XDG_CONFIG_HOME
|
||||||
|
set -gx GEMINI_CONTEXT_DIR $XDG_CONFIG_HOME/gemini
|
||||||
|
else
|
||||||
|
set -gx GEMINI_CONTEXT_DIR $HOME/.config/gemini
|
||||||
|
end
|
||||||
|
mkdir -p "$GEMINI_CONTEXT_DIR"
|
||||||
|
|
||||||
|
# Path to the source of truth
|
||||||
|
set -l gemini_zsh_script "$HOME/.skel/dotfiles/zshrc.d/gemini.zsh"
|
||||||
|
|
||||||
|
function _gemini_context_bridge
|
||||||
|
# Check if Zsh script exists
|
||||||
|
if not test -f "$gemini_zsh_script"
|
||||||
|
echo "Error: Gemini Zsh script not found at $gemini_zsh_script"
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# We use a temporary file to reliably pass the environment variable back
|
||||||
|
set -l env_file (mktemp -t gemini_context.XXXXXX)
|
||||||
|
|
||||||
|
# Run zsh with -e (exit on error):
|
||||||
|
# 1. Source the context manager
|
||||||
|
# 2. Run the requested command with arguments
|
||||||
|
# 3. Write the resulting GEMINI_CLI_HOME to the temp file
|
||||||
|
command zsh -ec "
|
||||||
|
source '$gemini_zsh_script'
|
||||||
|
$argv
|
||||||
|
echo \"\$GEMINI_CLI_HOME\" > '$env_file'
|
||||||
|
"
|
||||||
|
set -l zsh_status $status
|
||||||
|
|
||||||
|
# Only sync environment and clean up on success
|
||||||
|
if test $zsh_status -eq 0
|
||||||
|
set -l new_home (cat $env_file | string trim)
|
||||||
|
if test -n "$new_home"
|
||||||
|
set -gx GEMINI_CLI_HOME "$new_home"
|
||||||
|
else
|
||||||
|
set -e GEMINI_CLI_HOME
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
rm -f $env_file
|
||||||
|
return $zsh_status
|
||||||
|
end
|
||||||
|
|
||||||
|
# Wrapper functions
|
||||||
|
function gemini-context-use
|
||||||
|
_gemini_context_bridge "gemini-context-use $argv"
|
||||||
|
end
|
||||||
|
|
||||||
|
function gemini-context-create
|
||||||
|
_gemini_context_bridge "gemini-context-create $argv"
|
||||||
|
end
|
||||||
|
|
||||||
|
function gemini-context-list
|
||||||
|
_gemini_context_bridge "gemini-context-list $argv"
|
||||||
|
end
|
||||||
|
|
||||||
|
function gemini-context-delete
|
||||||
|
_gemini_context_bridge "gemini-context-delete $argv"
|
||||||
|
end
|
||||||
|
|
||||||
|
function gemini-context-rename
|
||||||
|
_gemini_context_bridge "gemini-context-rename $argv"
|
||||||
|
end
|
||||||
|
|
||||||
|
function gemini-context-edit
|
||||||
|
_gemini_context_bridge "gemini-context-edit $argv"
|
||||||
|
end
|
||||||
|
|
||||||
|
function gemini-context-current
|
||||||
|
_gemini_context_bridge "gemini-context-current $argv"
|
||||||
|
end
|
||||||
|
|
||||||
|
function gemini-context-unset
|
||||||
|
_gemini_context_bridge "gemini-context-unset $argv"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Aliases
|
||||||
|
alias gemctx='gemini-context-use'
|
||||||
|
|
||||||
|
# Completion
|
||||||
|
function _gemini_context_list
|
||||||
|
zsh -c "source '$gemini_zsh_script'; _gemini_context_list_internal"
|
||||||
|
end
|
||||||
|
|
||||||
|
complete -c gemini-context-use -f -a "(_gemini_context_list)"
|
||||||
|
complete -c gemctx -f -a "(_gemini_context_list)"
|
||||||
|
complete -c gemini-context-edit -f -a "(_gemini_context_list)"
|
||||||
|
complete -c gemini-context-delete -f -a "(_gemini_context_list)"
|
||||||
|
complete -c gemini-context-rename -f -a "(_gemini_context_list)"
|
||||||
39
dotfiles/config/fish/config.fish
Normal file
39
dotfiles/config/fish/config.fish
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
if test -x /opt/homebrew/bin/brew
|
||||||
|
/opt/homebrew/bin/brew shellenv fish | source
|
||||||
|
if test -d (brew --prefix)"/share/fish/completions"
|
||||||
|
set -p fish_complete_path (brew --prefix)/share/fish/completions
|
||||||
|
end
|
||||||
|
|
||||||
|
if test -d (brew --prefix)"/share/fish/vendor_completions.d"
|
||||||
|
set -p fish_complete_path (brew --prefix)/share/fish/vendor_completions.d
|
||||||
|
end
|
||||||
|
|
||||||
|
# mise, if installed
|
||||||
|
if type -q mise
|
||||||
|
mise activate fish | source
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if command -q starship
|
||||||
|
starship init fish --print-full-init | source
|
||||||
|
end
|
||||||
|
|
||||||
|
function install_fisher
|
||||||
|
if not test -e ~/.config/fish/functions/fisher.fish
|
||||||
|
echo "Installing Fisher for fish shell..."
|
||||||
|
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source
|
||||||
|
fisher install jorgebucaran/fisher
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if status --is-interactive
|
||||||
|
install_fisher
|
||||||
|
end
|
||||||
|
|
||||||
|
# Want these at the bottom to put them first in PATH
|
||||||
|
fish_add_path --move --path {$HOME}/bin
|
||||||
|
if test (uname) = "Darwin"
|
||||||
|
fish_add_path --move --path {$HOME}/bin/macos
|
||||||
|
else if test (uname) = "Linux"
|
||||||
|
fish_add_path --move --path {$HOME}/bin/linux
|
||||||
|
end
|
||||||
2
dotfiles/config/fish/fish_plugins
Normal file
2
dotfiles/config/fish/fish_plugins
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
jorgebucaran/fisher
|
||||||
|
wfxr/forgit
|
||||||
8
dotfiles/config/fish/functions/cdgr.fish
Normal file
8
dotfiles/config/fish/functions/cdgr.fish
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Change to the root of the git repository.
|
||||||
|
# If not in a git repo, do nothing.
|
||||||
|
function cdgr
|
||||||
|
set git_root (git rev-parse --show-toplevel 2>/dev/null)
|
||||||
|
if test -n "$git_root"
|
||||||
|
cd "$git_root"
|
||||||
|
end
|
||||||
|
end
|
||||||
15
dotfiles/config/gemini/settings.json
Normal file
15
dotfiles/config/gemini/settings.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"mode": "vim"
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"includeFiles": [
|
||||||
|
"/usr/local/google/home/davidtomaschik/.skel/GEMINI.md",
|
||||||
|
"/usr/local/google/home/davidtomaschik/.skel/AGENTS.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"experimental": {
|
||||||
|
"skills": true,
|
||||||
|
"planMode": true
|
||||||
|
}
|
||||||
|
}
|
||||||
12
dotfiles/config/gh/config.yml
Normal file
12
dotfiles/config/gh/config.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# What protocol to use when performing git operations. Supported values: ssh, https
|
||||||
|
git_protocol: https
|
||||||
|
# What editor gh should run when creating issues, pull requests, etc. If blank, will refer to environment.
|
||||||
|
editor: !!null vim
|
||||||
|
# When to interactively prompt. This is a global config that cannot be overridden by hostname. Supported values: enabled, disabled
|
||||||
|
prompt: enabled
|
||||||
|
# A pager program to send command output to, e.g. "less". Set the value to "cat" to disable the pager.
|
||||||
|
pager: !!null less -R
|
||||||
|
# Aliases allow you to create nicknames for gh commands
|
||||||
|
aliases:
|
||||||
|
co: pr checkout
|
||||||
|
version: "1"
|
||||||
64
dotfiles/config/htop/htoprc
Normal file
64
dotfiles/config/htop/htoprc
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# Beware! This file is rewritten by htop when settings are changed in the interface.
|
||||||
|
# The parser is also very primitive, and not human-friendly.
|
||||||
|
htop_version=3.4.1-3.4.1
|
||||||
|
config_reader_min_version=3
|
||||||
|
fields=0 48 17 18 38 39 40 2 46 47 49 1
|
||||||
|
hide_kernel_threads=1
|
||||||
|
hide_userland_threads=1
|
||||||
|
hide_running_in_container=0
|
||||||
|
shadow_other_users=0
|
||||||
|
show_thread_names=0
|
||||||
|
show_program_path=1
|
||||||
|
highlight_base_name=0
|
||||||
|
highlight_deleted_exe=1
|
||||||
|
shadow_distribution_path_prefix=0
|
||||||
|
highlight_megabytes=1
|
||||||
|
highlight_threads=1
|
||||||
|
highlight_changes=0
|
||||||
|
highlight_changes_delay_secs=5
|
||||||
|
find_comm_in_cmdline=1
|
||||||
|
strip_exe_from_cmdline=1
|
||||||
|
show_merged_command=0
|
||||||
|
header_margin=1
|
||||||
|
screen_tabs=0
|
||||||
|
detailed_cpu_time=0
|
||||||
|
cpu_count_from_one=1
|
||||||
|
show_cpu_usage=1
|
||||||
|
show_cpu_frequency=0
|
||||||
|
show_cpu_temperature=0
|
||||||
|
degree_fahrenheit=0
|
||||||
|
show_cached_memory=1
|
||||||
|
update_process_names=0
|
||||||
|
account_guest_in_cpu_meter=0
|
||||||
|
color_scheme=6
|
||||||
|
enable_mouse=1
|
||||||
|
delay=15
|
||||||
|
hide_function_bar=0
|
||||||
|
header_layout=two_50_50
|
||||||
|
column_meters_0=AllCPUs Memory Swap
|
||||||
|
column_meter_modes_0=1 1 1
|
||||||
|
column_meters_1=Tasks LoadAverage Uptime
|
||||||
|
column_meter_modes_1=2 2 2
|
||||||
|
tree_view=0
|
||||||
|
sort_key=46
|
||||||
|
tree_sort_key=0
|
||||||
|
sort_direction=-1
|
||||||
|
tree_sort_direction=1
|
||||||
|
tree_view_always_by_pid=0
|
||||||
|
all_branches_collapsed=0
|
||||||
|
screen:Main=PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command
|
||||||
|
.sort_key=PERCENT_CPU
|
||||||
|
.tree_sort_key=PID
|
||||||
|
.tree_view_always_by_pid=0
|
||||||
|
.tree_view=0
|
||||||
|
.sort_direction=-1
|
||||||
|
.tree_sort_direction=1
|
||||||
|
.all_branches_collapsed=0
|
||||||
|
screen:I/O=PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command
|
||||||
|
.sort_key=IO_RATE
|
||||||
|
.tree_sort_key=PID
|
||||||
|
.tree_view_always_by_pid=0
|
||||||
|
.tree_view=0
|
||||||
|
.sort_direction=-1
|
||||||
|
.tree_sort_direction=1
|
||||||
|
.all_branches_collapsed=0
|
||||||
@@ -87,6 +87,10 @@ bindsym $mod+Shift+8 move container to workspace 8
|
|||||||
bindsym $mod+Shift+9 move container to workspace 9
|
bindsym $mod+Shift+9 move container to workspace 9
|
||||||
bindsym $mod+Shift+0 move container to workspace 10
|
bindsym $mod+Shift+0 move container to workspace 10
|
||||||
|
|
||||||
|
# do some scratchpad
|
||||||
|
bindsym $mod+Shift+minus move scratchpad
|
||||||
|
bindsym $mod+minus scratchpad show
|
||||||
|
|
||||||
# Move workspaces between monitors
|
# Move workspaces between monitors
|
||||||
bindsym $mod+Shift+greater move workspace to output right
|
bindsym $mod+Shift+greater move workspace to output right
|
||||||
bindsym $mod+Shift+less move workspace to output left
|
bindsym $mod+Shift+less move workspace to output left
|
||||||
@@ -135,6 +139,8 @@ bindsym $mod+l exec \
|
|||||||
bash -c "i3lock -c 000000 && (sleep 2 && xset dpms force off) &"
|
bash -c "i3lock -c 000000 && (sleep 2 && xset dpms force off) &"
|
||||||
bindsym $alt+Control+l exec \
|
bindsym $alt+Control+l exec \
|
||||||
bash -c "i3lock -c 000000 && (sleep 2 && xset dpms force off) &"
|
bash -c "i3lock -c 000000 && (sleep 2 && xset dpms force off) &"
|
||||||
|
# suspend under systemd
|
||||||
|
bindsym $mod+Control+s exec --no-startup-id systemctl suspend
|
||||||
|
|
||||||
# things to start quickly
|
# things to start quickly
|
||||||
bindsym $mod+g exec /usr/bin/google-chrome-beta --password-store=gnome
|
bindsym $mod+g exec /usr/bin/google-chrome-beta --password-store=gnome
|
||||||
@@ -146,39 +152,33 @@ bindsym --whole-window $mod+button3 floating toggle
|
|||||||
|
|
||||||
# media keys
|
# media keys
|
||||||
# Pulse Audio controls
|
# Pulse Audio controls
|
||||||
bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume 0 +5%
|
bindsym XF86AudioRaiseVolume exec --no-startup-id ~/bin/pactl_helper volume +5%
|
||||||
bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume 0 -5%
|
bindsym XF86AudioLowerVolume exec --no-startup-id ~/bin/pactl_helper volume -5%
|
||||||
bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute 0 toggle
|
bindsym XF86AudioMute exec --no-startup-id ~/bin/pactl_helper mute toggle
|
||||||
|
bindsym XF86AudioMicMute exec --no-startup-id ~/bin/pactl_helper micmute toggle
|
||||||
|
bindsym F13 exec --no-startup-id ~/bin/pactl_helper micmute toggle
|
||||||
# Screen brightness controls
|
# Screen brightness controls
|
||||||
bindsym XF86MonBrightnessUp exec --no-startup-id xbacklight -inc 10
|
bindsym XF86MonBrightnessUp exec --no-startup-id xbacklight -inc 10
|
||||||
bindsym XF86MonBrightnessDown exec --no-startup-id xbacklight -dec 10
|
bindsym XF86MonBrightnessDown exec --no-startup-id xbacklight -dec 10
|
||||||
|
|
||||||
# screenshots
|
# screenshots
|
||||||
set $scrotformat %F-%T.png
|
# region/selection
|
||||||
set $screendir ~/Pictures/Screenshots
|
|
||||||
# whole screen
|
|
||||||
bindsym --release Print exec --no-startup-id \
|
bindsym --release Print exec --no-startup-id \
|
||||||
mkdir -p $screendir && \
|
~/bin/screenshot.sh region
|
||||||
/usr/bin/scrot $screendir/$scrotformat
|
# full screen
|
||||||
# current window
|
|
||||||
bindsym --release Shift+Print exec --no-startup-id \
|
bindsym --release Shift+Print exec --no-startup-id \
|
||||||
mkdir -p $screendir && \
|
~/bin/screenshot.sh full
|
||||||
/usr/bin/scrot -u $screendir/$scrotformat
|
# single window
|
||||||
# selection
|
|
||||||
bindsym --release $alt+Sys_Req exec --no-startup-id \
|
bindsym --release $alt+Sys_Req exec --no-startup-id \
|
||||||
mkdir -p $screendir && \
|
~/bin/screenshot.sh window
|
||||||
/usr/bin/scrot -s $screendir/$scrotformat
|
|
||||||
|
|
||||||
# useful utilities
|
# useful utilities
|
||||||
#exec --no-startup-id \
|
|
||||||
# (which xss-lock >/dev/null || /usr/bin/cinnamon-settings-daemon) &
|
|
||||||
exec --no-startup-id gnome-keyring-daemon --start --components=pkcs11,secrets
|
exec --no-startup-id gnome-keyring-daemon --start --components=pkcs11,secrets
|
||||||
# Make GTK fonts look great again
|
|
||||||
#exec --no-startup-id gnome-settings-daemon
|
|
||||||
#exec --no-startup-id (sleep 2; nm-applet) &
|
|
||||||
exec --no-startup-id xset r rate 200 20
|
exec --no-startup-id xset r rate 200 20
|
||||||
#exec --no-startup-id ~/bin/autostart.py
|
#exec --no-startup-id ~/bin/autostart.py
|
||||||
exec --no-startup-id dex --autostart --environment x-cinnamon
|
exec --no-startup-id dex --autostart --environment x-cinnamon
|
||||||
|
# Solaar for mouse
|
||||||
|
exec --no-startup-id sh -c 'command solaar -w hide || true'
|
||||||
|
|
||||||
# customize windows
|
# customize windows
|
||||||
for_window [window_role="pop-up"] floating enable
|
for_window [window_role="pop-up"] floating enable
|
||||||
|
|||||||
19
dotfiles/config/mise/config.toml
Normal file
19
dotfiles/config/mise/config.toml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[settings]
|
||||||
|
experimental = true
|
||||||
|
|
||||||
|
[settings.pipx]
|
||||||
|
uvx = true
|
||||||
|
|
||||||
|
[settings.python]
|
||||||
|
uv_venv_auto = true
|
||||||
|
|
||||||
|
[tools]
|
||||||
|
age = "latest"
|
||||||
|
usage = "latest"
|
||||||
|
uv = "0.11.8"
|
||||||
|
|
||||||
|
[hooks]
|
||||||
|
postinstall = "mise sync python --uv"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
UV_PROJECT_ENVIRONMENT = { value='{% if env.PROJECT_SLUG %}{{ env.HOME}}/.cache/uv-venvs/{{ env.PROJECT_SLUG }}{% endif %}', tools = true }
|
||||||
4
dotfiles/config/nvim/init.vim
Normal file
4
dotfiles/config/nvim/init.vim
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
" nvim config
|
||||||
|
set runtimepath^=~/.vim runtimepath+=~/.vim/after
|
||||||
|
let &packpath = &runtimepath
|
||||||
|
source ~/.vimrc
|
||||||
54
dotfiles/config/starship.toml
Normal file
54
dotfiles/config/starship.toml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
"$schema" = 'https://starship.rs/config-schema.json'
|
||||||
|
|
||||||
|
[directory]
|
||||||
|
fish_style_pwd_dir_length = 1
|
||||||
|
truncate_to_repo = false
|
||||||
|
|
||||||
|
[gcloud]
|
||||||
|
symbol = "️🇬️ "
|
||||||
|
format = "on [$symbol$project]($style) "
|
||||||
|
detect_env_vars = ["STARSHIP_SHOW_GCLOUD"]
|
||||||
|
|
||||||
|
[status]
|
||||||
|
disabled = false
|
||||||
|
symbol = "⛌"
|
||||||
|
|
||||||
|
[username]
|
||||||
|
show_always = true
|
||||||
|
|
||||||
|
[ruby]
|
||||||
|
detect_variables = []
|
||||||
|
|
||||||
|
[shell]
|
||||||
|
fish_indicator = '🐟 '
|
||||||
|
zsh_indicator = ''
|
||||||
|
bash_indicator = ''
|
||||||
|
powershell_indicator = '_'
|
||||||
|
unknown_indicator = '??'
|
||||||
|
style = 'cyan bold'
|
||||||
|
disabled = false
|
||||||
|
format = '[$indicator]($style)'
|
||||||
|
|
||||||
|
[pulumi]
|
||||||
|
disabled = true
|
||||||
|
|
||||||
|
[docker_context]
|
||||||
|
# really long paths when DOCKER_HOST is set
|
||||||
|
disabled = true
|
||||||
|
|
||||||
|
[kubernetes]
|
||||||
|
disabled = false
|
||||||
|
detect_folders = ["k8s"]
|
||||||
|
|
||||||
|
[custom.gemini_context]
|
||||||
|
description = "Displays the current Gemini CLI context"
|
||||||
|
when = "test -n \"$GEMINI_CLI_HOME\""
|
||||||
|
command = """
|
||||||
|
context_dir=\"${XDG_CONFIG_HOME:-$HOME/.config}/gemini\"
|
||||||
|
if [[ \"$GEMINI_CLI_HOME\" == $context_dir/* ]]; then
|
||||||
|
basename \"$GEMINI_CLI_HOME\"
|
||||||
|
fi
|
||||||
|
"""
|
||||||
|
style = "bold blue"
|
||||||
|
format = "♊[$output](blue) "
|
||||||
|
shell = ["/bin/sh", "-c"]
|
||||||
@@ -1 +0,0 @@
|
|||||||
/dev/null
|
|
||||||
15
dotfiles/config/user-dirs.dirs
Normal file
15
dotfiles/config/user-dirs.dirs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# This file is written by xdg-user-dirs-update
|
||||||
|
# If you want to change or add directories, just edit the line you're
|
||||||
|
# interested in. All local changes will be retained on the next run
|
||||||
|
# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
|
||||||
|
# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
|
||||||
|
# absolute path. No other format is supported.
|
||||||
|
#
|
||||||
|
XDG_DESKTOP_DIR="$HOME/Desktop"
|
||||||
|
XDG_DOWNLOAD_DIR="$HOME/Downloads"
|
||||||
|
XDG_TEMPLATES_DIR="$HOME/"
|
||||||
|
XDG_PUBLICSHARE_DIR="$HOME/Shared"
|
||||||
|
XDG_DOCUMENTS_DIR="$HOME/Documents"
|
||||||
|
XDG_MUSIC_DIR="$HOME/Music"
|
||||||
|
XDG_PICTURES_DIR="$HOME/Pictures"
|
||||||
|
XDG_VIDEOS_DIR="$HOME/Videos"
|
||||||
181
dotfiles/config/wezterm/wezterm.lua
Normal file
181
dotfiles/config/wezterm/wezterm.lua
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
|
||||||
|
% Total % Received % Xferd Average Speed Time Time Time Current
|
||||||
|
Dload Upload Total Spent Left Speed
|
||||||
|
|
||||||
|
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0-- https://wezfurlong.org/wezterm/config/files.html
|
||||||
|
-- https://alexplescan.com/posts/2024/08/10/wezterm/
|
||||||
|
-- https://github.com/wez/wezterm/issues/6112
|
||||||
|
-- https://github.com/wez/wezterm/issues/5754
|
||||||
|
local wezterm = require "wezterm"
|
||||||
|
local config = wezterm.config_builder()
|
||||||
|
local action = wezterm.action
|
||||||
|
local mux = wezterm.mux
|
||||||
|
|
||||||
|
config.audible_bell = "Disabled"
|
||||||
|
config.check_for_updates = false -- managed by brew
|
||||||
|
config.set_environment_variables = {
|
||||||
|
PATH = '/opt/homebrew/bin:/usr/local/bin/:' .. os.getenv('PATH')
|
||||||
|
}
|
||||||
|
|
||||||
|
local function scheme_for_appearance(a)
|
||||||
|
return a:find("Dark") and "Catppuccin Macchiato" or "Catppuccin Latte"
|
||||||
|
end
|
||||||
|
|
||||||
|
config.font = wezterm.font { family = 'JetBrains Mono', weight = 'Medium', harfbuzz_features = { 'calt=0', 'clig=0', 'liga=0' } }
|
||||||
|
config.font_size = 15.0
|
||||||
|
config.line_height = 1.0
|
||||||
|
config.bold_brightens_ansi_colors = true
|
||||||
|
config.color_scheme = scheme_for_appearance(wezterm.gui.get_appearance())
|
||||||
|
config.macos_window_background_blur = 20
|
||||||
|
config.window_background_opacity = 0.96
|
||||||
|
|
||||||
|
config.window_decorations = 'RESIZE|INTEGRATED_BUTTONS'
|
||||||
|
config.window_padding = { left = '0.5cell', right = '0.5cell', top = '1.5cell', bottom = '0.5cell' }
|
||||||
|
config.enable_tab_bar = true
|
||||||
|
config.use_fancy_tab_bar = false
|
||||||
|
config.tab_bar_at_bottom = true
|
||||||
|
config.tab_max_width = 32
|
||||||
|
config.show_new_tab_button_in_tab_bar = false
|
||||||
|
-- config.hide_tab_bar_if_only_one_tab = true -- sometimes procude wrong window size on maximize
|
||||||
|
|
||||||
|
config.default_cursor_style = 'BlinkingBar'
|
||||||
|
config.animation_fps = 1
|
||||||
|
config.cursor_blink_rate = 500
|
||||||
|
config.prefer_egl = true
|
||||||
|
config.max_fps = 60
|
||||||
|
|
||||||
|
config.enable_scroll_bar = true
|
||||||
|
config.scrollback_lines = 10000
|
||||||
|
|
||||||
|
-- makes wezterm to work like tmux; see also: https://bower.sh/zmx-session-persistence
|
||||||
|
config.default_gui_startup_args = { 'connect', 'unix' }
|
||||||
|
config.window_close_confirmation = 'NeverPrompt'
|
||||||
|
|
||||||
|
local function maximize_window(window)
|
||||||
|
if not window.gui_window then return end
|
||||||
|
|
||||||
|
local screen = wezterm.gui.screens().active
|
||||||
|
local guiwin = window:gui_window()
|
||||||
|
if not screen or not guiwin then return end
|
||||||
|
|
||||||
|
-- window:gui_window():maximize() -- have long animation
|
||||||
|
guiwin:set_position(screen.x, screen.y)
|
||||||
|
guiwin:set_inner_size(screen.width, screen.height)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- https://github.com/wez/wezterm/issues/3299#issuecomment-2145712082
|
||||||
|
wezterm.on("gui-startup", function(cmd)
|
||||||
|
local tab, pane, window = mux.spawn_window(cmd or {})
|
||||||
|
maximize_window(window)
|
||||||
|
end)
|
||||||
|
|
||||||
|
wezterm.on("gui-attached", function(window)
|
||||||
|
local workspace = mux.get_active_workspace()
|
||||||
|
for _, window in ipairs(mux.all_windows()) do
|
||||||
|
if window:get_workspace() == workspace then
|
||||||
|
maximize_window(window)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
wezterm.on("window-resized", function(window, pane)
|
||||||
|
maximize_window(window)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- see also https://wezterm.org/config/lua/wezterm/battery_info.html
|
||||||
|
wezterm.on("format-tab-title", function(tab, tabs, panes, config, hover, max_width)
|
||||||
|
local title = (tab.tab_title ~= "" and tab.tab_title) or tab.active_pane.title
|
||||||
|
title = title:lower()
|
||||||
|
|
||||||
|
local prefix = tostring(tab.tab_index + 1) .. ":"
|
||||||
|
local width = math.max(1, max_width - (#prefix + 2))
|
||||||
|
title = wezterm.truncate_right(title, width)
|
||||||
|
|
||||||
|
return " " .. prefix .. title .. " "
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- https://github.com/wezterm/wezterm/issues/1988#issuecomment-2462216249
|
||||||
|
local function search_cmd(window, pane)
|
||||||
|
window:perform_action(action.Search 'CurrentSelectionOrEmptyString', pane)
|
||||||
|
window:perform_action(action.Multiple {
|
||||||
|
action.CopyMode 'ClearPattern',
|
||||||
|
action.CopyMode 'ClearSelectionMode',
|
||||||
|
action.CopyMode 'MoveToScrollbackBottom'
|
||||||
|
}, pane)
|
||||||
|
end
|
||||||
|
|
||||||
|
config.keys = {
|
||||||
|
{ key = 'q', mods = 'CMD', action = wezterm.action.Nop }, -- prevent accidental quit
|
||||||
|
{ key = 't', mods = 'CMD', action = action.SpawnTab 'CurrentPaneDomain' },
|
||||||
|
{ key = 'd', mods = 'CMD', action = action.SplitHorizontal { domain = 'CurrentPaneDomain' } },
|
||||||
|
{ key = 'd', mods = 'CMD|SHIFT', action = action.SplitVertical { domain = 'CurrentPaneDomain' } },
|
||||||
|
{ key = 'k', mods = 'CMD', action = action.ClearScrollback 'ScrollbackAndViewport' },
|
||||||
|
{ key = 'w', mods = 'CMD', action = action.CloseCurrentPane { confirm
|
||||||
|
100 6869 100 6869 0 0 42178 0 --:--:-- --:--:-- --:--:-- 42401
|
||||||
|
= false } },
|
||||||
|
{ key = 'w', mods = 'CMD|SHIFT', action = action.CloseCurrentTab { confirm = false } },
|
||||||
|
{ key = 'a', mods = 'CMD', action = action.SelectTextAtMouseCursor 'SemanticZone', },
|
||||||
|
{ key = 'LeftArrow', mods = 'CMD', action = action.SendKey { key = 'Home' } },
|
||||||
|
{ key = 'RightArrow', mods = 'CMD', action = action.SendKey { key = 'End' } },
|
||||||
|
{ key = 'Backspace', mods = 'CMD', action = action.SendKey({ mods = "CTRL", key = "u" }) },
|
||||||
|
{ key = 'Backspace', mods = 'OPT', action = action.SendKey({ mods = "CTRL", key = "w" }) },
|
||||||
|
{ key = 'P', mods = 'CMD|SHIFT', action = action.ActivateCommandPalette },
|
||||||
|
{ key = 'f', mods = 'CMD', action = wezterm.action_callback(search_cmd) },
|
||||||
|
{ key = ',', mods = 'CMD', action = action.SpawnCommandInNewTab { cwd = wezterm.home_dir, args = { 'code', wezterm.config_file } } },
|
||||||
|
{ key = 'E', mods = 'CMD|SHIFT', action = action.PromptInputLine {
|
||||||
|
description = 'Enter tab title (empty to unset):',
|
||||||
|
action = wezterm.action_callback(function(window, _, line)
|
||||||
|
window:active_tab():set_title(line)
|
||||||
|
end),
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
config.mouse_bindings = {
|
||||||
|
{ event = { Up = { streak = 1, button = "Left" } }, mods = "NONE", action = action.Nop },
|
||||||
|
{ event = { Up = { streak = 1, button = "Left" } }, mods = "CMD", action = action.OpenLinkAtMouseCursor },
|
||||||
|
-- Disable CMD + LeftClick window drag (make it behave like normal select)
|
||||||
|
-- { event = { Drag = { streak = 1, button = 'Left' } }, mods = "CMD", action = action.Nop },
|
||||||
|
{ event = { Drag = { streak = 1, button = "Left" } }, mods = "CMD", action = action.ExtendSelectionToMouseCursor("Cell") },
|
||||||
|
}
|
||||||
|
|
||||||
|
-- https://code.visualstudio.com/docs/configure/command-line#_opening-vs-code-with-urls
|
||||||
|
-- path-symbols: [\w@\.\/\-\[\]\(\)]
|
||||||
|
|
||||||
|
local function path_exists(path)
|
||||||
|
local ok, _, _ = wezterm.run_child_process { "test", "-e", path }
|
||||||
|
return ok
|
||||||
|
end
|
||||||
|
|
||||||
|
config.hyperlink_rules = wezterm.default_hyperlink_rules()
|
||||||
|
-- config.hyperlink_rules = {}
|
||||||
|
|
||||||
|
table.insert(config.hyperlink_rules, {
|
||||||
|
regex = [[((?:[\w@\.\/\-\[\]\(\)]+\/)+[\w@\.\/\-\[\]\(\)]+\.\w+)\b]],
|
||||||
|
format = "vscode://file/$PWD/$1",
|
||||||
|
highlight = 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
table.insert(config.hyperlink_rules, {
|
||||||
|
regex = [[((?:[\w@\.\/\-\[\]\(\)]+\/)+[\w@\.\/\-\[\]\(\)]+\.\w+):(\d+):(\d+)]],
|
||||||
|
format = "vscode://file/$PWD/$1:$2:$3",
|
||||||
|
highlight = 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
wezterm.on("open-uri", function(window, pane, uri)
|
||||||
|
if uri:find("$PWD") then
|
||||||
|
local cwd_uri = pane:get_current_working_dir()
|
||||||
|
local before, after = uri:match("^(.-)$PWD/(.+)$")
|
||||||
|
wezterm.log_info(before, after, path_exists(after))
|
||||||
|
if path_exists(after) then
|
||||||
|
uri = before .. after
|
||||||
|
else
|
||||||
|
uri = uri:gsub("$PWD", cwd_uri.file_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
wezterm.log_info(uri)
|
||||||
|
wezterm.open_with(uri)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end)
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<channel name="xfce4-keyboard-shortcuts" version="1.0">
|
||||||
|
<property name="commands" type="empty">
|
||||||
|
<property name="default" type="empty">
|
||||||
|
<property name="<Alt>F1" type="empty"/>
|
||||||
|
<property name="<Alt>F2" type="empty">
|
||||||
|
<property name="startup-notify" type="empty"/>
|
||||||
|
</property>
|
||||||
|
<property name="<Alt>F3" type="empty">
|
||||||
|
<property name="startup-notify" type="empty"/>
|
||||||
|
</property>
|
||||||
|
<property name="<Primary><Alt>Delete" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>l" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>t" type="empty"/>
|
||||||
|
<property name="XF86Display" type="empty"/>
|
||||||
|
<property name="<Super>p" type="empty"/>
|
||||||
|
<property name="<Primary>Escape" type="empty"/>
|
||||||
|
<property name="XF86WWW" type="empty"/>
|
||||||
|
<property name="HomePage" type="empty"/>
|
||||||
|
<property name="XF86Mail" type="empty"/>
|
||||||
|
<property name="Print" type="empty"/>
|
||||||
|
<property name="<Alt>Print" type="empty"/>
|
||||||
|
<property name="<Shift>Print" type="empty"/>
|
||||||
|
<property name="<Super>e" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>f" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>Escape" type="empty"/>
|
||||||
|
<property name="<Primary><Shift>Escape" type="empty"/>
|
||||||
|
<property name="<Super>r" type="empty">
|
||||||
|
<property name="startup-notify" type="empty"/>
|
||||||
|
</property>
|
||||||
|
<property name="<Alt><Super>s" type="empty"/>
|
||||||
|
</property>
|
||||||
|
<property name="custom" type="empty">
|
||||||
|
<property name="<Alt>F2" type="string" value="xfce4-appfinder --collapsed">
|
||||||
|
<property name="startup-notify" type="bool" value="true"/>
|
||||||
|
</property>
|
||||||
|
<property name="<Super>r" type="string" value="xfce4-appfinder -c">
|
||||||
|
<property name="startup-notify" type="bool" value="true"/>
|
||||||
|
</property>
|
||||||
|
<property name="XF86WWW" type="string" value="exo-open --launch WebBrowser"/>
|
||||||
|
<property name="XF86Mail" type="string" value="exo-open --launch MailReader"/>
|
||||||
|
<property name="<Alt>F3" type="string" value="xfce4-appfinder">
|
||||||
|
<property name="startup-notify" type="bool" value="true"/>
|
||||||
|
</property>
|
||||||
|
<property name="<Primary>Escape" type="string" value="xfdesktop --menu"/>
|
||||||
|
<property name="<Primary><Alt>Delete" type="string" value="xfce4-session-logout"/>
|
||||||
|
<property name="<Primary><Alt>t" type="string" value="exo-open --launch TerminalEmulator"/>
|
||||||
|
<property name="<Primary><Alt>f" type="string" value="thunar"/>
|
||||||
|
<property name="<Primary><Alt>l" type="string" value="xflock4"/>
|
||||||
|
<property name="<Alt>F1" type="string" value="xfce4-popup-applicationsmenu"/>
|
||||||
|
<property name="<Super>p" type="string" value="xfce4-display-settings --minimal"/>
|
||||||
|
<property name="<Primary><Shift>Escape" type="string" value="xfce4-taskmanager"/>
|
||||||
|
<property name="<Super>e" type="string" value="thunar"/>
|
||||||
|
<property name="<Primary><Alt>Escape" type="string" value="xkill"/>
|
||||||
|
<property name="HomePage" type="string" value="exo-open --launch WebBrowser"/>
|
||||||
|
<property name="XF86Display" type="string" value="xfce4-display-settings --minimal"/>
|
||||||
|
<property name="override" type="bool" value="true"/>
|
||||||
|
<property name="<Super>l" type="string" value="xflock4"/>
|
||||||
|
<property name="Print" type="string" value="flameshot gui"/>
|
||||||
|
<property name="<Shift>Print" type="string" value="flameshot full"/>
|
||||||
|
</property>
|
||||||
|
</property>
|
||||||
|
<property name="xfwm4" type="empty">
|
||||||
|
<property name="default" type="empty">
|
||||||
|
<property name="<Alt>Insert" type="empty"/>
|
||||||
|
<property name="Escape" type="empty"/>
|
||||||
|
<property name="Left" type="empty"/>
|
||||||
|
<property name="Right" type="empty"/>
|
||||||
|
<property name="Up" type="empty"/>
|
||||||
|
<property name="Down" type="empty"/>
|
||||||
|
<property name="<Alt>Tab" type="empty"/>
|
||||||
|
<property name="<Alt><Shift>Tab" type="empty"/>
|
||||||
|
<property name="<Alt>Delete" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>Down" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>Left" type="empty"/>
|
||||||
|
<property name="<Shift><Alt>Page_Down" type="empty"/>
|
||||||
|
<property name="<Alt>F4" type="empty"/>
|
||||||
|
<property name="<Alt>F6" type="empty"/>
|
||||||
|
<property name="<Alt>F7" type="empty"/>
|
||||||
|
<property name="<Alt>F8" type="empty"/>
|
||||||
|
<property name="<Alt>F9" type="empty"/>
|
||||||
|
<property name="<Alt>F10" type="empty"/>
|
||||||
|
<property name="<Alt>F11" type="empty"/>
|
||||||
|
<property name="<Alt>F12" type="empty"/>
|
||||||
|
<property name="<Primary><Shift><Alt>Left" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>End" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>Home" type="empty"/>
|
||||||
|
<property name="<Primary><Shift><Alt>Right" type="empty"/>
|
||||||
|
<property name="<Primary><Shift><Alt>Up" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>KP_1" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>KP_2" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>KP_3" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>KP_4" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>KP_5" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>KP_6" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>KP_7" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>KP_8" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>KP_9" type="empty"/>
|
||||||
|
<property name="<Alt>space" type="empty"/>
|
||||||
|
<property name="<Shift><Alt>Page_Up" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>Right" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>d" type="empty"/>
|
||||||
|
<property name="<Primary><Alt>Up" type="empty"/>
|
||||||
|
<property name="<Super>Tab" type="empty"/>
|
||||||
|
<property name="<Primary>F1" type="empty"/>
|
||||||
|
<property name="<Primary>F2" type="empty"/>
|
||||||
|
<property name="<Primary>F3" type="empty"/>
|
||||||
|
<property name="<Primary>F4" type="empty"/>
|
||||||
|
<property name="<Primary>F5" type="empty"/>
|
||||||
|
<property name="<Primary>F6" type="empty"/>
|
||||||
|
<property name="<Primary>F7" type="empty"/>
|
||||||
|
<property name="<Primary>F8" type="empty"/>
|
||||||
|
<property name="<Primary>F9" type="empty"/>
|
||||||
|
<property name="<Primary>F10" type="empty"/>
|
||||||
|
<property name="<Primary>F11" type="empty"/>
|
||||||
|
<property name="<Primary>F12" type="empty"/>
|
||||||
|
<property name="<Super>KP_Left" type="empty"/>
|
||||||
|
<property name="<Super>KP_Right" type="empty"/>
|
||||||
|
<property name="<Super>KP_Down" type="empty"/>
|
||||||
|
<property name="<Super>KP_Up" type="empty"/>
|
||||||
|
<property name="<Super>KP_Page_Up" type="empty"/>
|
||||||
|
<property name="<Super>KP_Home" type="empty"/>
|
||||||
|
<property name="<Super>KP_End" type="empty"/>
|
||||||
|
<property name="<Super>KP_Next" type="empty"/>
|
||||||
|
</property>
|
||||||
|
<property name="custom" type="empty">
|
||||||
|
<property name="<Primary>F12" type="string" value="workspace_12_key"/>
|
||||||
|
<property name="<Alt>F4" type="string" value="close_window_key"/>
|
||||||
|
<property name="<Primary><Alt>KP_3" type="string" value="move_window_workspace_3_key"/>
|
||||||
|
<property name="<Primary><Alt>Down" type="string" value="down_workspace_key"/>
|
||||||
|
<property name="<Primary><Alt>KP_9" type="string" value="move_window_workspace_9_key"/>
|
||||||
|
<property name="<Primary><Alt>End" type="string" value="move_window_next_workspace_key"/>
|
||||||
|
<property name="<Primary><Shift><Alt>Left" type="string" value="move_window_left_key"/>
|
||||||
|
<property name="<Primary><Alt>KP_4" type="string" value="move_window_workspace_4_key"/>
|
||||||
|
<property name="Right" type="string" value="right_key"/>
|
||||||
|
<property name="Down" type="string" value="down_key"/>
|
||||||
|
<property name="<Shift><Alt>Page_Down" type="string" value="lower_window_key"/>
|
||||||
|
<property name="<Alt>Tab" type="string" value="cycle_windows_key"/>
|
||||||
|
<property name="<Primary><Shift><Alt>Right" type="string" value="move_window_right_key"/>
|
||||||
|
<property name="<Primary><Alt>Right" type="string" value="right_workspace_key"/>
|
||||||
|
<property name="<Alt>F6" type="string" value="stick_window_key"/>
|
||||||
|
<property name="<Primary><Alt>KP_5" type="string" value="move_window_workspace_5_key"/>
|
||||||
|
<property name="<Primary>F11" type="string" value="workspace_11_key"/>
|
||||||
|
<property name="<Alt>F10" type="string" value="maximize_window_key"/>
|
||||||
|
<property name="<Alt>Delete" type="string" value="del_workspace_key"/>
|
||||||
|
<property name="<Super>Tab" type="string" value="switch_window_key"/>
|
||||||
|
<property name="<Primary><Alt>d" type="string" value="show_desktop_key"/>
|
||||||
|
<property name="<Super>KP_Page_Up" type="string" value="tile_up_right_key"/>
|
||||||
|
<property name="<Alt>F7" type="string" value="move_window_key"/>
|
||||||
|
<property name="Up" type="string" value="up_key"/>
|
||||||
|
<property name="<Primary><Alt>KP_6" type="string" value="move_window_workspace_6_key"/>
|
||||||
|
<property name="<Alt>F11" type="string" value="fullscreen_key"/>
|
||||||
|
<property name="<Alt>space" type="string" value="popup_menu_key"/>
|
||||||
|
<property name="<Super>KP_Home" type="string" value="tile_up_left_key"/>
|
||||||
|
<property name="Escape" type="string" value="cancel_key"/>
|
||||||
|
<property name="<Primary><Alt>KP_1" type="string" value="move_window_workspace_1_key"/>
|
||||||
|
<property name="<Shift><Alt>Page_Up" type="string" value="raise_window_key"/>
|
||||||
|
<property name="<Primary><Alt>Home" type="string" value="move_window_prev_workspace_key"/>
|
||||||
|
<property name="<Alt><Shift>Tab" type="string" value="cycle_reverse_windows_key"/>
|
||||||
|
<property name="<Primary><Alt>Left" type="string" value="left_workspace_key"/>
|
||||||
|
<property name="<Alt>F12" type="string" value="above_key"/>
|
||||||
|
<property name="<Primary><Shift><Alt>Up" type="string" value="move_window_up_key"/>
|
||||||
|
<property name="<Alt>F8" type="string" value="resize_window_key"/>
|
||||||
|
<property name="<Primary><Alt>KP_7" type="string" value="move_window_workspace_7_key"/>
|
||||||
|
<property name="<Primary><Alt>KP_2" type="string" value="move_window_workspace_2_key"/>
|
||||||
|
<property name="<Super>KP_End" type="string" value="tile_down_left_key"/>
|
||||||
|
<property name="<Primary><Alt>Up" type="string" value="up_workspace_key"/>
|
||||||
|
<property name="<Alt>F9" type="string" value="hide_window_key"/>
|
||||||
|
<property name="Left" type="string" value="left_key"/>
|
||||||
|
<property name="<Primary><Alt>KP_8" type="string" value="move_window_workspace_8_key"/>
|
||||||
|
<property name="<Alt>Insert" type="string" value="add_workspace_key"/>
|
||||||
|
<property name="override" type="bool" value="true"/>
|
||||||
|
<property name="<Super>1" type="string" value="workspace_1_key"/>
|
||||||
|
<property name="<Super>2" type="string" value="workspace_2_key"/>
|
||||||
|
<property name="<Super>3" type="string" value="workspace_3_key"/>
|
||||||
|
<property name="<Super>4" type="string" value="workspace_4_key"/>
|
||||||
|
<property name="<Super>5" type="string" value="workspace_5_key"/>
|
||||||
|
<property name="<Super>6" type="string" value="workspace_6_key"/>
|
||||||
|
<property name="<Super>7" type="string" value="workspace_7_key"/>
|
||||||
|
<property name="<Super>8" type="string" value="workspace_8_key"/>
|
||||||
|
<property name="<Super>9" type="string" value="workspace_9_key"/>
|
||||||
|
<property name="<Super>0" type="string" value="workspace_10_key"/>
|
||||||
|
<property name="<Super>Left" type="string" value="tile_left_key"/>
|
||||||
|
<property name="<Super>Right" type="string" value="tile_right_key"/>
|
||||||
|
<property name="<Super>Up" type="string" value="tile_down_key"/>
|
||||||
|
<property name="<Super>Down" type="string" value="tile_up_key"/>
|
||||||
|
<property name="<Super>Page_Down" type="string" value="tile_down_right_key"/>
|
||||||
|
</property>
|
||||||
|
</property>
|
||||||
|
<property name="providers" type="array">
|
||||||
|
<value type="string" value="xfwm4"/>
|
||||||
|
<value type="string" value="commands"/>
|
||||||
|
</property>
|
||||||
|
</channel>
|
||||||
93
dotfiles/config/xfce4/xfconf/xfce-perchannel-xml/xfwm4.xml
Normal file
93
dotfiles/config/xfce4/xfconf/xfce-perchannel-xml/xfwm4.xml
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<channel name="xfwm4" version="1.0">
|
||||||
|
<property name="general" type="empty">
|
||||||
|
<property name="activate_action" type="string" value="switch"/>
|
||||||
|
<property name="borderless_maximize" type="bool" value="true"/>
|
||||||
|
<property name="box_move" type="bool" value="false"/>
|
||||||
|
<property name="box_resize" type="bool" value="false"/>
|
||||||
|
<property name="button_layout" type="string" value="O|SHMC"/>
|
||||||
|
<property name="button_offset" type="int" value="0"/>
|
||||||
|
<property name="button_spacing" type="int" value="0"/>
|
||||||
|
<property name="click_to_focus" type="bool" value="false"/>
|
||||||
|
<property name="cycle_apps_only" type="bool" value="false"/>
|
||||||
|
<property name="cycle_draw_frame" type="bool" value="true"/>
|
||||||
|
<property name="cycle_raise" type="bool" value="false"/>
|
||||||
|
<property name="cycle_hidden" type="bool" value="true"/>
|
||||||
|
<property name="cycle_minimum" type="bool" value="true"/>
|
||||||
|
<property name="cycle_minimized" type="bool" value="false"/>
|
||||||
|
<property name="cycle_preview" type="bool" value="true"/>
|
||||||
|
<property name="cycle_tabwin_mode" type="int" value="0"/>
|
||||||
|
<property name="cycle_workspaces" type="bool" value="false"/>
|
||||||
|
<property name="double_click_action" type="string" value="maximize"/>
|
||||||
|
<property name="double_click_distance" type="int" value="5"/>
|
||||||
|
<property name="double_click_time" type="int" value="250"/>
|
||||||
|
<property name="easy_click" type="string" value="Alt"/>
|
||||||
|
<property name="focus_delay" type="int" value="316"/>
|
||||||
|
<property name="focus_hint" type="bool" value="true"/>
|
||||||
|
<property name="focus_new" type="bool" value="true"/>
|
||||||
|
<property name="frame_opacity" type="int" value="100"/>
|
||||||
|
<property name="frame_border_top" type="int" value="0"/>
|
||||||
|
<property name="full_width_title" type="bool" value="true"/>
|
||||||
|
<property name="horiz_scroll_opacity" type="bool" value="false"/>
|
||||||
|
<property name="inactive_opacity" type="int" value="100"/>
|
||||||
|
<property name="maximized_offset" type="int" value="0"/>
|
||||||
|
<property name="mousewheel_rollup" type="bool" value="true"/>
|
||||||
|
<property name="move_opacity" type="int" value="100"/>
|
||||||
|
<property name="placement_mode" type="string" value="center"/>
|
||||||
|
<property name="placement_ratio" type="int" value="20"/>
|
||||||
|
<property name="popup_opacity" type="int" value="100"/>
|
||||||
|
<property name="prevent_focus_stealing" type="bool" value="false"/>
|
||||||
|
<property name="raise_delay" type="int" value="250"/>
|
||||||
|
<property name="raise_on_click" type="bool" value="true"/>
|
||||||
|
<property name="raise_on_focus" type="bool" value="false"/>
|
||||||
|
<property name="raise_with_any_button" type="bool" value="true"/>
|
||||||
|
<property name="repeat_urgent_blink" type="bool" value="false"/>
|
||||||
|
<property name="resize_opacity" type="int" value="100"/>
|
||||||
|
<property name="scroll_workspaces" type="bool" value="true"/>
|
||||||
|
<property name="shadow_delta_height" type="int" value="0"/>
|
||||||
|
<property name="shadow_delta_width" type="int" value="0"/>
|
||||||
|
<property name="shadow_delta_x" type="int" value="0"/>
|
||||||
|
<property name="shadow_delta_y" type="int" value="-3"/>
|
||||||
|
<property name="shadow_opacity" type="int" value="50"/>
|
||||||
|
<property name="show_app_icon" type="bool" value="false"/>
|
||||||
|
<property name="show_dock_shadow" type="bool" value="true"/>
|
||||||
|
<property name="show_frame_shadow" type="bool" value="true"/>
|
||||||
|
<property name="show_popup_shadow" type="bool" value="false"/>
|
||||||
|
<property name="snap_resist" type="bool" value="false"/>
|
||||||
|
<property name="snap_to_border" type="bool" value="true"/>
|
||||||
|
<property name="snap_to_windows" type="bool" value="false"/>
|
||||||
|
<property name="snap_width" type="int" value="10"/>
|
||||||
|
<property name="vblank_mode" type="string" value="auto"/>
|
||||||
|
<property name="theme" type="string" value="Default"/>
|
||||||
|
<property name="tile_on_move" type="bool" value="true"/>
|
||||||
|
<property name="title_alignment" type="string" value="center"/>
|
||||||
|
<property name="title_font" type="string" value="Sans Bold 9"/>
|
||||||
|
<property name="title_horizontal_offset" type="int" value="0"/>
|
||||||
|
<property name="titleless_maximize" type="bool" value="false"/>
|
||||||
|
<property name="title_shadow_active" type="string" value="false"/>
|
||||||
|
<property name="title_shadow_inactive" type="string" value="false"/>
|
||||||
|
<property name="title_vertical_offset_active" type="int" value="0"/>
|
||||||
|
<property name="title_vertical_offset_inactive" type="int" value="0"/>
|
||||||
|
<property name="toggle_workspaces" type="bool" value="false"/>
|
||||||
|
<property name="unredirect_overlays" type="bool" value="true"/>
|
||||||
|
<property name="urgent_blink" type="bool" value="false"/>
|
||||||
|
<property name="use_compositing" type="bool" value="true"/>
|
||||||
|
<property name="workspace_count" type="int" value="6"/>
|
||||||
|
<property name="wrap_cycle" type="bool" value="true"/>
|
||||||
|
<property name="wrap_layout" type="bool" value="true"/>
|
||||||
|
<property name="wrap_resistance" type="int" value="10"/>
|
||||||
|
<property name="wrap_windows" type="bool" value="false"/>
|
||||||
|
<property name="wrap_workspaces" type="bool" value="false"/>
|
||||||
|
<property name="zoom_desktop" type="bool" value="true"/>
|
||||||
|
<property name="zoom_pointer" type="bool" value="true"/>
|
||||||
|
<property name="workspace_names" type="array">
|
||||||
|
<value type="string" value="Workspace 1"/>
|
||||||
|
<value type="string" value="Workspace 2"/>
|
||||||
|
<value type="string" value="Workspace 3"/>
|
||||||
|
<value type="string" value="Workspace 4"/>
|
||||||
|
<value type="string" value="Workspace 5"/>
|
||||||
|
<value type="string" value="Workspace 6"/>
|
||||||
|
</property>
|
||||||
|
</property>
|
||||||
|
</channel>
|
||||||
6
dotfiles/config/ycm_extra_conf.py
Normal file
6
dotfiles/config/ycm_extra_conf.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
def Settings( **kwargs ):
|
||||||
|
client_data = kwargs[ 'client_data' ]
|
||||||
|
return {
|
||||||
|
'interpreter_path': client_data[ 'g:ycm_python_interpreter_path' ] or '/usr/bin/python3',
|
||||||
|
'sys_path': client_data[ 'g:ycm_python_sys_path' ]
|
||||||
|
}
|
||||||
82
dotfiles/env
82
dotfiles/env
@@ -1,82 +0,0 @@
|
|||||||
# Sourced by zshrc as well as bash.
|
|
||||||
# Should only use POSIX shell constructs.
|
|
||||||
|
|
||||||
umask 027
|
|
||||||
|
|
||||||
# Paths and preferences
|
|
||||||
export PYTHONPATH="$HOME/.python:$PYTHONPATH"
|
|
||||||
export GOPATH="$HOME/Projects/Go:/usr/share/gocode"
|
|
||||||
export PATH="$HOME/bin:/sbin:/usr/sbin:$PATH:$GOPATH/bin"
|
|
||||||
export VISUAL=vim
|
|
||||||
export EDITOR=vim
|
|
||||||
export DEBEMAIL="david@systemoverlord.com"
|
|
||||||
export DEBFULLNAME="David Tomaschik"
|
|
||||||
export LESS="-MR"
|
|
||||||
export QUOTING_STYLE="literal" # Coreutils quotes
|
|
||||||
|
|
||||||
# Fix gnome-terminal
|
|
||||||
if [ "$TERM" = "xterm" ] && [ "$COLORTERM" = "gnome-terminal" ] ; then
|
|
||||||
# Requires `ncurses-base` package for terminfo.
|
|
||||||
export TERM="xterm-256color"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Terminal preferences for i3
|
|
||||||
if [ -z "${TERMINAL}" ] ; then
|
|
||||||
for t in urxvt gnome-terminal; do
|
|
||||||
if TERMINAL=$(command -v ${t}) ; then
|
|
||||||
export TERMINAL
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Browser preferences
|
|
||||||
if [ -z "${BROWSER}" ] ; then
|
|
||||||
for t in google-chrome-beta google-chrome firefox ; do
|
|
||||||
if BROWSER=$(command -v ${t}); then
|
|
||||||
export BROWSER
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For virtualenvwrapper
|
|
||||||
export WORKON_HOME=$HOME/.virtualenvs
|
|
||||||
export VIRTUALENVWRAPPER_VIRTUALENV_ARGS='--no-site-packages'
|
|
||||||
|
|
||||||
# GPG full key id
|
|
||||||
export GPG_ID=7FD58D9A196DCEEEAD671F94F4D7A7915DEA789B
|
|
||||||
|
|
||||||
# Setup locale
|
|
||||||
if test -x /usr/bin/locale ; then
|
|
||||||
for l in en_US.utf8 C.UTF-8 C ; do
|
|
||||||
if /usr/bin/locale -a | grep -q "${l}" ; then
|
|
||||||
export LC_CTYPE=${l}
|
|
||||||
export LC_NUMERIC=${l}
|
|
||||||
export LC_TIME=${l}
|
|
||||||
export LC_MONETARY=${l}
|
|
||||||
export LC_MESSAGES=${l}
|
|
||||||
export LC_PAPER=${l}
|
|
||||||
export LC_NAME=${l}
|
|
||||||
export LC_ADDRESS=${l}
|
|
||||||
export LC_TELEPHONE=${l}
|
|
||||||
export LC_MEASUREMENT=${l}
|
|
||||||
export LC_IDENTIFICATION=${l}
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
export LC_CTYPE=C
|
|
||||||
export LC_NUMERIC=C
|
|
||||||
export LC_TIME=C
|
|
||||||
export LC_MONETARY=C
|
|
||||||
export LC_MESSAGES=C
|
|
||||||
export LC_PAPER=C
|
|
||||||
export LC_NAME=C
|
|
||||||
export LC_ADDRESS=C
|
|
||||||
export LC_TELEPHONE=C
|
|
||||||
export LC_MEASUREMENT=C
|
|
||||||
export LC_IDENTIFICATION=C
|
|
||||||
fi
|
|
||||||
export LC_COLLATE=C
|
|
||||||
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
test -e "$HOME/.localenv" && . "$HOME/.localenv"
|
|
||||||
22
dotfiles/gemini/settings.json
Normal file
22
dotfiles/gemini/settings.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"security": {
|
||||||
|
"auth": {
|
||||||
|
"selectedType": "oauth-personal"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"fileName": [
|
||||||
|
"AGENTS.md",
|
||||||
|
"GEMINI.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"selectedAuthType": "gemini-api-key",
|
||||||
|
"general": {
|
||||||
|
"vimMode": true,
|
||||||
|
"sessionRetention": {
|
||||||
|
"enabled": true,
|
||||||
|
"maxAge": "30d",
|
||||||
|
"warningAcknowledged": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
[user]
|
[user]
|
||||||
name = David Tomaschik
|
name = David Tomaschik
|
||||||
email = david@systemoverlord.com
|
useConfigOnly = true
|
||||||
signingKey = 0x5DEA789B
|
|
||||||
|
|
||||||
[core]
|
[core]
|
||||||
excludesfile = ~/.gitignore
|
excludesfile = ~/.gitignore
|
||||||
editor = vim
|
editor = vim
|
||||||
whitespace = trailing-space,space-before-tab
|
whitespace = trailing-space,space-before-tab
|
||||||
|
pager = command -v delta >/dev/null 2>&1 && delta || less -eFiJM~ -j3
|
||||||
|
|
||||||
[color]
|
[color]
|
||||||
diff = auto
|
diff = auto
|
||||||
@@ -14,18 +14,25 @@
|
|||||||
|
|
||||||
[diff]
|
[diff]
|
||||||
tool = vimdiff
|
tool = vimdiff
|
||||||
|
colorMoved = default
|
||||||
|
|
||||||
[difftool]
|
[difftool]
|
||||||
prompt = false
|
prompt = false
|
||||||
|
|
||||||
|
[difftool "difftastic"]
|
||||||
|
cmd = difft "$LOCAL" "$REMOTE"
|
||||||
|
|
||||||
[alias]
|
[alias]
|
||||||
st = status
|
st = status
|
||||||
last = log -1 HEAD
|
last = log -1 HEAD
|
||||||
# Thanks to
|
# Thanks to
|
||||||
# http://durdn.com/blog/2012/11/22/must-have-git-aliases-advanced-examples/
|
# http://durdn.com/blog/2012/11/22/must-have-git-aliases-advanced-examples/
|
||||||
ls = log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate
|
logs = log --pretty=format:"%C(yellow)%h%Cred%d %Creset%s%Cblue [%cn]" --decorate
|
||||||
ll = log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate --numstat
|
lg = log -p
|
||||||
|
ll = log --pretty=format:"%C(yellow)%h%Cred%d %Creset%s%Cblue [%cn]" --decorate --numstat
|
||||||
files = ls-files
|
files = ls-files
|
||||||
|
ls = ls-files
|
||||||
|
lol = log --graph --pretty=format:'%C(yellow)%h%Creset %an: %s - %Creset %C(yellow)%d%Creset %Cblue(%cr)%Creset' --abbrev-commit --date=relative
|
||||||
f = "!git ls-files | grep -i"
|
f = "!git ls-files | grep -i"
|
||||||
logtree = log --graph --oneline --decorate --all
|
logtree = log --graph --oneline --decorate --all
|
||||||
|
|
||||||
@@ -39,27 +46,61 @@
|
|||||||
user = matir
|
user = matir
|
||||||
|
|
||||||
# Site specific config
|
# Site specific config
|
||||||
[url "https://github.com./"]
|
[url "https://github.com/"]
|
||||||
insteadOf = "github:"
|
insteadOf = "github:"
|
||||||
insteadOf = "github://"
|
insteadOf = "github://"
|
||||||
|
|
||||||
[url "ssh://git@github.com/"]
|
[url "ssh://git@github.com/"]
|
||||||
pushInsteadOf = "github:"
|
pushInsteadOf = "github:"
|
||||||
pushInsteadOf = "github://"
|
pushInsteadOf = "github://"
|
||||||
|
pushInsteadOf = "https://github.com/"
|
||||||
|
|
||||||
[url "git://gist.github.com/"]
|
[url "git://gist.github.com/"]
|
||||||
insteadOf = "gist:"
|
insteadOf = "gist:"
|
||||||
|
|
||||||
[url "git@gist.github.com:"]
|
[url "git@gist.github.com:"]
|
||||||
pushInsteadOf = "gist:"
|
pushInsteadOf = "gist:"
|
||||||
pushInsteadOf = "git://gist.github.com/"
|
pushInsteadOf = "git://gist.github.com/"
|
||||||
|
|
||||||
[credential]
|
[credential]
|
||||||
helper = cache --timeout=36000
|
helper = cache --timeout=36000
|
||||||
|
|
||||||
[receive]
|
[receive]
|
||||||
denyCurrentBranch = updateInstead
|
denyCurrentBranch = updateInstead
|
||||||
|
|
||||||
[merge]
|
[merge]
|
||||||
tool = vimdiff
|
tool = vimdiff
|
||||||
conflictstyle = diff3
|
conflictstyle = diff3
|
||||||
|
|
||||||
[mergetool]
|
[mergetool]
|
||||||
prompt = false
|
prompt = false
|
||||||
|
|
||||||
|
[pull]
|
||||||
|
rebase = false
|
||||||
|
|
||||||
|
[init]
|
||||||
|
defaultBranch = main
|
||||||
|
|
||||||
|
[interactive]
|
||||||
|
diffFilter = command -v delta >/dev/null 2>&1 && delta || cat
|
||||||
|
|
||||||
|
[delta]
|
||||||
|
navigate = true
|
||||||
|
line-numbers = true
|
||||||
|
|
||||||
|
[filter "lfs"]
|
||||||
|
required = true
|
||||||
|
clean = git-lfs clean -- %f
|
||||||
|
smudge = git-lfs smudge -- %f
|
||||||
|
process = git-lfs filter-process
|
||||||
|
|
||||||
|
[include]
|
||||||
|
path = ~/.gitconfig.d/aliases
|
||||||
|
path = ~/.gitconfig.d/override
|
||||||
|
path = ~/.gitconfig.d/local
|
||||||
|
|
||||||
|
[includeIf "gitdir/i:~/personal/"]
|
||||||
|
path = ~/.gitconfig.d/personal
|
||||||
|
|
||||||
|
[rerere]
|
||||||
|
enabled = true
|
||||||
|
|||||||
9
dotfiles/gitconfig.d/aliases
Normal file
9
dotfiles/gitconfig.d/aliases
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[alias]
|
||||||
|
commit-assumed = "!f() { \
|
||||||
|
file=\"$1\"; \
|
||||||
|
shift; \
|
||||||
|
git update-index --no-assume-unchanged \"$file\" && \
|
||||||
|
git add \"$file\" && \
|
||||||
|
git commit \"$@\" && \
|
||||||
|
git update-index --assume-unchanged \"$file\"; \
|
||||||
|
}; f"
|
||||||
5
dotfiles/gitconfig.d/override
Normal file
5
dotfiles/gitconfig.d/override
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[user]
|
||||||
|
email = david@systemoverlord.com
|
||||||
|
|
||||||
|
[github]
|
||||||
|
user = matir
|
||||||
2
dotfiles/gitconfig.d/personal
Normal file
2
dotfiles/gitconfig.d/personal
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[user]
|
||||||
|
email = david@systemoverlord.com
|
||||||
@@ -17,6 +17,24 @@ Thumbs.db
|
|||||||
# Try to avoid accidentally checking in private keys
|
# Try to avoid accidentally checking in private keys
|
||||||
id_rsa
|
id_rsa
|
||||||
id_ecdsa
|
id_ecdsa
|
||||||
|
id_ed25519
|
||||||
|
|
||||||
# Kicad backup files
|
# Kicad backup files
|
||||||
*.kicad_pcb-bak
|
*.kicad_pcb-bak
|
||||||
|
|
||||||
|
# Mypy cache path
|
||||||
|
.mypy_cache
|
||||||
|
|
||||||
|
# These files should basically never be committed
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Mise local
|
||||||
|
mise.local.toml
|
||||||
|
|
||||||
|
# AI Tool Configs
|
||||||
|
.aider/
|
||||||
|
.claude/
|
||||||
|
.continue/
|
||||||
|
.copilot/
|
||||||
|
.cursor/
|
||||||
|
.gemini/
|
||||||
|
|||||||
@@ -2,4 +2,3 @@ default-cache-ttl 7200
|
|||||||
default-cache-ttl-ssh 7200
|
default-cache-ttl-ssh 7200
|
||||||
max-cache-ttl 86400
|
max-cache-ttl 86400
|
||||||
max-cache-ttl-ssh 86400
|
max-cache-ttl-ssh 86400
|
||||||
enable-ssh-support
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use-agent
|
use-agent
|
||||||
# HKPS requires gnupg-curl for gpg1
|
# HKPS requires gnupg-curl for gpg1
|
||||||
keyserver hkps://keys.openpgp.org
|
keyserver hkps://keyserver.ubuntu.com
|
||||||
keyserver-options auto-key-retrieve no-honor-keyserver-url
|
keyserver-options auto-key-retrieve no-honor-keyserver-url
|
||||||
auto-key-locate keyserver
|
auto-key-locate keyserver
|
||||||
personal-digest-preferences SHA256
|
personal-digest-preferences SHA256
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
try:
|
try:
|
||||||
import os, IPython
|
import os, IPython
|
||||||
|
from traitlets import config
|
||||||
os.environ['PYTHONSTARTUP'] = '' # Prevent running this again
|
os.environ['PYTHONSTARTUP'] = '' # Prevent running this again
|
||||||
IPython.start_ipython()
|
cfg = config.Config()
|
||||||
|
cfg.TerminalIPythonApp.display_banner = False
|
||||||
|
IPython.start_ipython(config=cfg)
|
||||||
raise SystemExit
|
raise SystemExit
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|||||||
60
dotfiles/ipython/profile_default/startup/05-venv-manager.py
Normal file
60
dotfiles/ipython/profile_default/startup/05-venv-manager.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from IPython.terminal import prompts
|
||||||
|
|
||||||
|
def _setup_environment():
|
||||||
|
ip = get_ipython()
|
||||||
|
if not ip:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 1. SEARCH LOGIC: Walk up the tree for a venv
|
||||||
|
active_venv_name = None
|
||||||
|
curr = os.getcwd()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
for venv_name in ['.venv', 'env', 'venv']:
|
||||||
|
venv_path = os.path.join(curr, venv_name)
|
||||||
|
if os.path.isdir(venv_path):
|
||||||
|
# Platform-specific site-packages path
|
||||||
|
py_ver = f"python{sys.version_info.major}.{sys.version_info.minor}"
|
||||||
|
site_pkgs = os.path.join(venv_path, 'lib', py_ver, 'site-packages')
|
||||||
|
|
||||||
|
if os.path.exists(site_pkgs):
|
||||||
|
if site_pkgs not in sys.path:
|
||||||
|
sys.path.insert(0, site_pkgs)
|
||||||
|
sys.prefix = venv_path
|
||||||
|
active_venv_name = os.path.basename(venv_path)
|
||||||
|
break
|
||||||
|
|
||||||
|
if active_venv_name or os.path.exists(os.path.join(curr, '.git')):
|
||||||
|
break
|
||||||
|
|
||||||
|
parent = os.path.dirname(curr)
|
||||||
|
if parent == curr:
|
||||||
|
break
|
||||||
|
curr = parent
|
||||||
|
|
||||||
|
# 2. PROMPT LOGIC: Inject venv name into the UI
|
||||||
|
class VenvPrompts(prompts.Prompts):
|
||||||
|
|
||||||
|
_prompts = prompts
|
||||||
|
|
||||||
|
def in_prompt_tokens(self):
|
||||||
|
tokens = []
|
||||||
|
if active_venv_name:
|
||||||
|
tokens.append((self._prompts.Token.Prompt, f'({active_venv_name}) '))
|
||||||
|
|
||||||
|
tokens.extend([
|
||||||
|
(self._prompts.Token.Prompt, 'In ['),
|
||||||
|
(self._prompts.Token.PromptNum, str(self.shell.execution_count)),
|
||||||
|
(self._prompts.Token.Prompt, ']: '),
|
||||||
|
])
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
ip.prompts = VenvPrompts(ip)
|
||||||
|
|
||||||
|
# Execute the setup
|
||||||
|
_setup_environment()
|
||||||
|
|
||||||
|
# cleanup
|
||||||
|
del _setup_environment, prompts
|
||||||
@@ -1,415 +0,0 @@
|
|||||||
servers = (
|
|
||||||
{
|
|
||||||
address = "ircs.overthewire.org";
|
|
||||||
chatnet = "overthewire";
|
|
||||||
port = "6697";
|
|
||||||
use_ssl = "yes";
|
|
||||||
ssl_verify = "no";
|
|
||||||
autoconnect = "yes";
|
|
||||||
},
|
|
||||||
{
|
|
||||||
address = "irc.secfo.org";
|
|
||||||
chatnet = "secfo";
|
|
||||||
port = "7000";
|
|
||||||
use_ssl = "yes";
|
|
||||||
ssl_verify = "no";
|
|
||||||
autoconnect = "yes";
|
|
||||||
},
|
|
||||||
{
|
|
||||||
address = "irc.hackint.eu";
|
|
||||||
chatnet = "hackint";
|
|
||||||
port = "9999";
|
|
||||||
use_ssl = "yes";
|
|
||||||
ssl_verify = "no";
|
|
||||||
autoconnect = "yes";
|
|
||||||
},
|
|
||||||
{
|
|
||||||
address = "irc.geekshed.net";
|
|
||||||
chatnet = "geekshed";
|
|
||||||
port = "6697";
|
|
||||||
use_ssl = "yes";
|
|
||||||
ssl_verify = "no";
|
|
||||||
autoconnect = "no";
|
|
||||||
},
|
|
||||||
{
|
|
||||||
address = "irc.rpis.ec";
|
|
||||||
chatnet = "rpisec";
|
|
||||||
port = "6697";
|
|
||||||
use_ssl = "yes";
|
|
||||||
ssl_verify = "no";
|
|
||||||
autoconnect = "yes";
|
|
||||||
},
|
|
||||||
{
|
|
||||||
address = "chat.freenode.net";
|
|
||||||
chatnet = "freenode";
|
|
||||||
port = "7000";
|
|
||||||
use_ssl = "yes";
|
|
||||||
ssl_cert = "~/.irssi/matir.pem";
|
|
||||||
ssl_verify = "yes";
|
|
||||||
ssl_capath = "/etc/ssl/certs";
|
|
||||||
family = "inet";
|
|
||||||
autoconnect = "yes";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
chatnets = {
|
|
||||||
OFTC = {
|
|
||||||
type = "IRC";
|
|
||||||
max_kicks = "1";
|
|
||||||
max_msgs = "3";
|
|
||||||
max_whois = "30";
|
|
||||||
};
|
|
||||||
overthewire = { type = "IRC"; nick = "Matir"; };
|
|
||||||
secfo = { type = "IRC"; };
|
|
||||||
hackint = { type = "IRC"; nick = "Matir"; };
|
|
||||||
freenode = {
|
|
||||||
type = "IRC";
|
|
||||||
nick = "Matir";
|
|
||||||
username = "matir";
|
|
||||||
realname = "Matir";
|
|
||||||
max_kicks = "1";
|
|
||||||
max_msgs = "4";
|
|
||||||
max_modes = "4";
|
|
||||||
max_whois = "1";
|
|
||||||
};
|
|
||||||
geekshed = {
|
|
||||||
type = "IRC";
|
|
||||||
nick = "KF4MDV";
|
|
||||||
username = "kf4mdv";
|
|
||||||
realname = "David";
|
|
||||||
};
|
|
||||||
rpisec = { type = "IRC"; };
|
|
||||||
};
|
|
||||||
|
|
||||||
channels = (
|
|
||||||
{ name = "#kali-linux"; chatnet = "freenode"; autojoin = "yes"; },
|
|
||||||
{ name = "#hak5"; chatnet = "secfo"; autojoin = "yes"; },
|
|
||||||
{ name = "#wargames"; chatnet = "overthewire"; autojoin = "yes"; },
|
|
||||||
{ name = "#social"; chatnet = "overthewire"; autojoin = "yes"; },
|
|
||||||
{ name = "#openvpn"; chatnet = "freenode"; autojoin = "yes"; },
|
|
||||||
{ name = "#amateria"; chatnet = "overthewire"; autojoin = "yes"; },
|
|
||||||
{ name = "#io"; chatnet = "overthewire"; autojoin = "yes"; },
|
|
||||||
{ name = "#radare"; chatnet = "freenode"; autojoin = "yes"; },
|
|
||||||
{ name = "#vulnhub"; chatnet = "freenode"; autojoin = "yes"; },
|
|
||||||
{ name = "#redditnet"; chatnet = "geekshed"; autojoin = "yes"; },
|
|
||||||
{ name = "#rpisec"; chatnet = "rpisec"; autojoin = "yes"; },
|
|
||||||
{ name = "#offsec"; chatnet = "freenode"; autojoin = "yes"; },
|
|
||||||
{ name = "#offtopicsec"; chatnet = "freenode"; autojoin = "yes"; }
|
|
||||||
);
|
|
||||||
|
|
||||||
aliases = {
|
|
||||||
J = "join";
|
|
||||||
WJOIN = "join -window";
|
|
||||||
WQUERY = "query -window";
|
|
||||||
LEAVE = "part";
|
|
||||||
BYE = "quit";
|
|
||||||
EXIT = "quit";
|
|
||||||
SIGNOFF = "quit";
|
|
||||||
DESCRIBE = "action";
|
|
||||||
DATE = "time";
|
|
||||||
HOST = "userhost";
|
|
||||||
LAST = "lastlog";
|
|
||||||
SAY = "msg *";
|
|
||||||
WI = "whois";
|
|
||||||
WII = "whois $0 $0";
|
|
||||||
WW = "whowas";
|
|
||||||
W = "who";
|
|
||||||
N = "names";
|
|
||||||
M = "msg";
|
|
||||||
T = "topic";
|
|
||||||
C = "clear";
|
|
||||||
CL = "clear";
|
|
||||||
K = "kick";
|
|
||||||
KB = "kickban";
|
|
||||||
KN = "knockout";
|
|
||||||
BANS = "ban";
|
|
||||||
B = "ban";
|
|
||||||
MUB = "unban *";
|
|
||||||
UB = "unban";
|
|
||||||
IG = "ignore";
|
|
||||||
UNIG = "unignore";
|
|
||||||
SB = "scrollback";
|
|
||||||
UMODE = "mode $N";
|
|
||||||
WC = "window close";
|
|
||||||
WN = "window new hide";
|
|
||||||
SV = "say Irssi $J ($V) - http://irssi.org/";
|
|
||||||
GOTO = "sb goto";
|
|
||||||
CHAT = "dcc chat";
|
|
||||||
RUN = "SCRIPT LOAD";
|
|
||||||
CALC = "exec - if command -v bc >/dev/null 2>&1\\; then printf '%s=' '$*'\\; echo '$*' | bc -l\\; else echo bc was not found\\; fi";
|
|
||||||
SBAR = "STATUSBAR";
|
|
||||||
INVITELIST = "mode $C +I";
|
|
||||||
Q = "QUERY";
|
|
||||||
"MANUAL-WINDOWS" = "set use_status_window off;set autocreate_windows off;set autocreate_query_level none;set autoclose_windows off;set reuse_unused_windows on;save";
|
|
||||||
EXEMPTLIST = "mode $C +e";
|
|
||||||
ATAG = "WINDOW SERVER";
|
|
||||||
UNSET = "set -clear";
|
|
||||||
RESET = "set -default";
|
|
||||||
};
|
|
||||||
|
|
||||||
statusbar = {
|
|
||||||
# formats:
|
|
||||||
# when using {templates}, the template is shown only if it's argument isn't
|
|
||||||
# empty unless no argument is given. for example {sb} is printed always,
|
|
||||||
# but {sb $T} is printed only if $T isn't empty.
|
|
||||||
|
|
||||||
items = {
|
|
||||||
# start/end text in statusbars
|
|
||||||
barstart = "{sbstart}";
|
|
||||||
barend = "{sbend}";
|
|
||||||
|
|
||||||
topicbarstart = "{topicsbstart}";
|
|
||||||
topicbarend = "{topicsbend}";
|
|
||||||
|
|
||||||
# treated "normally", you could change the time/user name to whatever
|
|
||||||
time = "{sb $Z}";
|
|
||||||
user = "{sb {sbnickmode $cumode}$N{sbmode $usermode}{sbaway $A}}";
|
|
||||||
|
|
||||||
# treated specially .. window is printed with non-empty windows,
|
|
||||||
# window_empty is printed with empty windows
|
|
||||||
window = "{sb $winref:$tag/$itemname{sbmode $M}}";
|
|
||||||
window_empty = "{sb $winref{sbservertag $tag}}";
|
|
||||||
prompt = "{prompt $[.15]itemname}";
|
|
||||||
prompt_empty = "{prompt $winname}";
|
|
||||||
topic = " $topic";
|
|
||||||
topic_empty = " Irssi v$J - http://www.irssi.org";
|
|
||||||
|
|
||||||
# all of these treated specially, they're only displayed when needed
|
|
||||||
lag = "{sb Lag: $0-}";
|
|
||||||
act = "{sb Act: $0-}";
|
|
||||||
more = "-- more --";
|
|
||||||
};
|
|
||||||
|
|
||||||
# there's two type of statusbars. root statusbars are either at the top
|
|
||||||
# of the screen or at the bottom of the screen. window statusbars are at
|
|
||||||
# the top/bottom of each split window in screen.
|
|
||||||
default = {
|
|
||||||
# the "default statusbar" to be displayed at the bottom of the window.
|
|
||||||
# contains all the normal items.
|
|
||||||
window = {
|
|
||||||
disabled = "yes";
|
|
||||||
|
|
||||||
# window, root
|
|
||||||
type = "window";
|
|
||||||
# top, bottom
|
|
||||||
placement = "bottom";
|
|
||||||
# number
|
|
||||||
position = "1";
|
|
||||||
# active, inactive, always
|
|
||||||
visible = "active";
|
|
||||||
|
|
||||||
# list of items in statusbar in the display order
|
|
||||||
items = {
|
|
||||||
barstart = { priority = "100"; };
|
|
||||||
time = { };
|
|
||||||
user = { };
|
|
||||||
window = { };
|
|
||||||
window_empty = { };
|
|
||||||
lag = { priority = "-1"; };
|
|
||||||
more = { priority = "-1"; alignment = "right"; };
|
|
||||||
barend = { priority = "100"; alignment = "right"; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# statusbar to use in inactive split windows
|
|
||||||
window_inact = {
|
|
||||||
type = "window";
|
|
||||||
placement = "bottom";
|
|
||||||
position = "1";
|
|
||||||
visible = "inactive";
|
|
||||||
items = {
|
|
||||||
barstart = { priority = "100"; };
|
|
||||||
window = { };
|
|
||||||
window_empty = { };
|
|
||||||
more = { priority = "-1"; alignment = "right"; };
|
|
||||||
barend = { priority = "100"; alignment = "right"; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# we treat input line as yet another statusbar :) It's possible to
|
|
||||||
# add other items before or after the input line item.
|
|
||||||
prompt = {
|
|
||||||
type = "root";
|
|
||||||
placement = "bottom";
|
|
||||||
# we want to be at the bottom always
|
|
||||||
position = "100";
|
|
||||||
visible = "always";
|
|
||||||
items = {
|
|
||||||
prompt = { priority = "-1"; };
|
|
||||||
prompt_empty = { priority = "-1"; };
|
|
||||||
# treated specially, this is the real input line.
|
|
||||||
input = { priority = "10"; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# topicbar
|
|
||||||
topic = {
|
|
||||||
type = "root";
|
|
||||||
placement = "top";
|
|
||||||
position = "1";
|
|
||||||
visible = "always";
|
|
||||||
items = {
|
|
||||||
topicbarstart = { priority = "100"; };
|
|
||||||
time = { priority = "100"; };
|
|
||||||
topic = { };
|
|
||||||
topic_empty = { };
|
|
||||||
window_empty = { alignment = "right"; };
|
|
||||||
user = { priority = "100"; alignment = "right"; };
|
|
||||||
topicbarend = { priority = "100"; alignment = "right"; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
placement = { items = { }; disabled = "yes"; };
|
|
||||||
remove = { disabled = "yes"; };
|
|
||||||
awl_0 = {
|
|
||||||
items = {
|
|
||||||
barstart = { priority = "100"; };
|
|
||||||
awl_0 = { };
|
|
||||||
barend = { priority = "100"; alignment = "right"; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
awl_1 = {
|
|
||||||
items = {
|
|
||||||
barstart = { priority = "100"; };
|
|
||||||
awl_1 = { };
|
|
||||||
barend = { priority = "100"; alignment = "right"; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
awl_2 = {
|
|
||||||
items = {
|
|
||||||
barstart = { priority = "100"; };
|
|
||||||
awl_2 = { };
|
|
||||||
barend = { priority = "100"; alignment = "right"; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
settings = {
|
|
||||||
core = {
|
|
||||||
real_name = "Matir";
|
|
||||||
user_name = "matir";
|
|
||||||
nick = "Matir";
|
|
||||||
log_timestamp = "%Y%m%d %H%M";
|
|
||||||
};
|
|
||||||
"fe-text" = { actlist_sort = "refnum"; };
|
|
||||||
"fe-common/core" = {
|
|
||||||
autolog = "yes";
|
|
||||||
autolog_path = "~/irclogs/%Y/%m/$tag/$0.log";
|
|
||||||
autoclose_query = "1800";
|
|
||||||
autocreate_own_query = "no";
|
|
||||||
show_nickmode_empty = "no";
|
|
||||||
term_charset = "utf-8";
|
|
||||||
};
|
|
||||||
"perl/core/scripts" = {
|
|
||||||
awl_shared_sbar = "OFF";
|
|
||||||
awl_viewer = "no";
|
|
||||||
awl_block = "-15";
|
|
||||||
awl_maxlines = "3";
|
|
||||||
awl_height_adjust = "2";
|
|
||||||
awl_hide_empty = "0";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
windows = {
|
|
||||||
1 = { immortal = "yes"; name = "(status)"; level = "ALL"; };
|
|
||||||
2 = {
|
|
||||||
items = (
|
|
||||||
{
|
|
||||||
type = "CHANNEL";
|
|
||||||
chat_type = "IRC";
|
|
||||||
name = "#kali-linux";
|
|
||||||
tag = "freenode";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
3 = {
|
|
||||||
items = (
|
|
||||||
{
|
|
||||||
type = "CHANNEL";
|
|
||||||
chat_type = "IRC";
|
|
||||||
name = "#radare";
|
|
||||||
tag = "freenode";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
4 = {
|
|
||||||
items = (
|
|
||||||
{
|
|
||||||
type = "CHANNEL";
|
|
||||||
chat_type = "IRC";
|
|
||||||
name = "#vulnhub";
|
|
||||||
tag = "freenode";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
5 = {
|
|
||||||
items = (
|
|
||||||
{
|
|
||||||
type = "CHANNEL";
|
|
||||||
chat_type = "IRC";
|
|
||||||
name = "#openvpn";
|
|
||||||
tag = "freenode";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
6 = {
|
|
||||||
items = (
|
|
||||||
{
|
|
||||||
type = "CHANNEL";
|
|
||||||
chat_type = "IRC";
|
|
||||||
name = "#wargames";
|
|
||||||
tag = "overthewire";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
7 = {
|
|
||||||
items = (
|
|
||||||
{
|
|
||||||
type = "CHANNEL";
|
|
||||||
chat_type = "IRC";
|
|
||||||
name = "#social";
|
|
||||||
tag = "overthewire";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
8 = {
|
|
||||||
items = (
|
|
||||||
{
|
|
||||||
type = "CHANNEL";
|
|
||||||
chat_type = "IRC";
|
|
||||||
name = "#amateria";
|
|
||||||
tag = "overthewire";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
9 = {
|
|
||||||
items = (
|
|
||||||
{
|
|
||||||
type = "CHANNEL";
|
|
||||||
chat_type = "IRC";
|
|
||||||
name = "#io";
|
|
||||||
tag = "overthewire";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
10 = {
|
|
||||||
items = (
|
|
||||||
{
|
|
||||||
type = "CHANNEL";
|
|
||||||
chat_type = "IRC";
|
|
||||||
name = "#RedditNet";
|
|
||||||
tag = "geekshed";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
11 = {
|
|
||||||
items = (
|
|
||||||
{
|
|
||||||
type = "CHANNEL";
|
|
||||||
chat_type = "IRC";
|
|
||||||
name = "#hak5";
|
|
||||||
tag = "secfo";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
mainwindows = { 1 = { first_line = "1"; lines = "78"; }; };
|
|
||||||
logs = { };
|
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
# When testing changes, the easiest way to reload the theme is with /RELOAD.
|
|
||||||
# This reloads the configuration file too, so if you did any changes remember
|
|
||||||
# to /SAVE it first. Remember also that /SAVE overwrites the theme file with
|
|
||||||
# old data so keep backups :)
|
|
||||||
|
|
||||||
# TEMPLATES:
|
|
||||||
|
|
||||||
# The real text formats that irssi uses are the ones you can find with
|
|
||||||
# /FORMAT command. Back in the old days all the colors and texts were mixed
|
|
||||||
# up in those formats, and it was really hard to change the colors since you
|
|
||||||
# might have had to change them in tens of different places. So, then came
|
|
||||||
# this templating system.
|
|
||||||
|
|
||||||
# Now the /FORMATs don't have any colors in them, and they also have very
|
|
||||||
# little other styling. Most of the stuff you need to change is in this
|
|
||||||
# theme file. If you can't change something here, you can always go back
|
|
||||||
# to change the /FORMATs directly, they're also saved in these .theme files.
|
|
||||||
|
|
||||||
# So .. the templates. They're those {blahblah} parts you see all over the
|
|
||||||
# /FORMATs and here. Their usage is simply {name parameter1 parameter2}.
|
|
||||||
# When irssi sees this kind of text, it goes to find "name" from abstracts
|
|
||||||
# block below and sets "parameter1" into $0 and "parameter2" into $1 (you
|
|
||||||
# can have more parameters of course). Templates can have subtemplates.
|
|
||||||
# Here's a small example:
|
|
||||||
# /FORMAT format hello {colorify {underline world}}
|
|
||||||
# abstracts = { colorify = "%G$0-%n"; underline = "%U$0-%U"; }
|
|
||||||
# When irssi expands the templates in "format", the final string would be:
|
|
||||||
# hello %G%Uworld%U%n
|
|
||||||
# ie. underlined bright green "world" text.
|
|
||||||
# and why "$0-", why not "$0"? $0 would only mean the first parameter,
|
|
||||||
# $0- means all the parameters. With {underline hello world} you'd really
|
|
||||||
# want to underline both of the words, not just the hello (and world would
|
|
||||||
# actually be removed entirely).
|
|
||||||
|
|
||||||
# COLORS:
|
|
||||||
|
|
||||||
# You can find definitions for the color format codes in docs/formats.txt.
|
|
||||||
|
|
||||||
# There's one difference here though. %n format. Normally it means the
|
|
||||||
# default color of the terminal (white mostly), but here it means the
|
|
||||||
# "reset color back to the one it was in higher template". For example
|
|
||||||
# if there was /FORMAT test %g{foo}bar, and foo = "%Y$0%n", irssi would
|
|
||||||
# print yellow "foo" (as set with %Y) but "bar" would be green, which was
|
|
||||||
# set at the beginning before the {foo} template. If there wasn't the %g
|
|
||||||
# at start, the normal behaviour of %n would occur. If you _really_ want
|
|
||||||
# to use the terminal's default color, use %N.
|
|
||||||
|
|
||||||
#############################################################################
|
|
||||||
|
|
||||||
# default foreground color (%N) - -1 is the "default terminal color"
|
|
||||||
default_color = "-1";
|
|
||||||
|
|
||||||
# print timestamp/servertag at the end of line, not at beginning
|
|
||||||
info_eol = "false";
|
|
||||||
|
|
||||||
# these characters are automatically replaced with specified color
|
|
||||||
# (dark grey by default)
|
|
||||||
replaces = { "[]=" = "%K$*%n"; };
|
|
||||||
|
|
||||||
abstracts = {
|
|
||||||
##
|
|
||||||
## generic
|
|
||||||
##
|
|
||||||
|
|
||||||
# text to insert at the beginning of each non-message line
|
|
||||||
line_start = "%B-%n!%B-%n ";
|
|
||||||
|
|
||||||
# timestamp styling, nothing by default
|
|
||||||
timestamp = "$*";
|
|
||||||
|
|
||||||
# any kind of text that needs hilighting, default is to bold
|
|
||||||
hilight = "%_$*%_";
|
|
||||||
|
|
||||||
# any kind of error message, default is bright red
|
|
||||||
error = "%R$*%n";
|
|
||||||
|
|
||||||
# channel name is printed
|
|
||||||
channel = "%_$*%_";
|
|
||||||
|
|
||||||
# nick is printed
|
|
||||||
nick = "%_$*%_";
|
|
||||||
|
|
||||||
# nick host is printed
|
|
||||||
nickhost = "[$*]";
|
|
||||||
|
|
||||||
# server name is printed
|
|
||||||
server = "%_$*%_";
|
|
||||||
|
|
||||||
# some kind of comment is printed
|
|
||||||
comment = "[$*]";
|
|
||||||
|
|
||||||
# reason for something is printed (part, quit, kick, ..)
|
|
||||||
reason = "{comment $*}";
|
|
||||||
|
|
||||||
# mode change is printed ([+o nick])
|
|
||||||
mode = "{comment $*}";
|
|
||||||
|
|
||||||
##
|
|
||||||
## channel specific messages
|
|
||||||
##
|
|
||||||
|
|
||||||
# highlighted nick/host is printed (joins)
|
|
||||||
channick_hilight = "%C$*%n";
|
|
||||||
chanhost_hilight = "{nickhost %c$*%n}";
|
|
||||||
|
|
||||||
# nick/host is printed (parts, quits, etc.)
|
|
||||||
channick = "%c$*%n";
|
|
||||||
chanhost = "{nickhost $*}";
|
|
||||||
|
|
||||||
# highlighted channel name is printed
|
|
||||||
channelhilight = "%c$*%n";
|
|
||||||
|
|
||||||
# ban/ban exception/invite list mask is printed
|
|
||||||
ban = "%c$*%n";
|
|
||||||
|
|
||||||
##
|
|
||||||
## messages
|
|
||||||
##
|
|
||||||
|
|
||||||
# the basic styling of how to print message, $0 = nick mode, $1 = nick
|
|
||||||
msgnick = "%K<%n$0$1-%K>%n %|";
|
|
||||||
|
|
||||||
# message from you is printed. "msgownnick" specifies the styling of the
|
|
||||||
# nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the
|
|
||||||
# whole line.
|
|
||||||
|
|
||||||
# Example1: You want the message text to be green:
|
|
||||||
# ownmsgnick = "{msgnick $0 $1-}%g";
|
|
||||||
# Example2.1: You want < and > chars to be yellow:
|
|
||||||
# ownmsgnick = "%Y{msgnick $0 $1-%Y}%n";
|
|
||||||
# (you'll also have to remove <> from replaces list above)
|
|
||||||
# Example2.2: But you still want to keep <> grey for other messages:
|
|
||||||
# pubmsgnick = "%K{msgnick $0 $1-%K}%n";
|
|
||||||
# pubmsgmenick = "%K{msgnick $0 $1-%K}%n";
|
|
||||||
# pubmsghinick = "%K{msgnick $1 $0$2-%n%K}%n";
|
|
||||||
# ownprivmsgnick = "%K{msgnick $*%K}%n";
|
|
||||||
# privmsgnick = "%K{msgnick %R$*%K}%n";
|
|
||||||
|
|
||||||
# $0 = nick mode, $1 = nick
|
|
||||||
ownmsgnick = "{msgnick $0 $1-}";
|
|
||||||
ownnick = "%_$*%n";
|
|
||||||
|
|
||||||
# public message in channel, $0 = nick mode, $1 = nick
|
|
||||||
pubmsgnick = "{msgnick $0 $1-}";
|
|
||||||
pubnick = "%N$*%n";
|
|
||||||
|
|
||||||
# public message in channel meant for me, $0 = nick mode, $1 = nick
|
|
||||||
pubmsgmenick = "{msgnick $0 $1-}";
|
|
||||||
menick = "%Y$*%n";
|
|
||||||
|
|
||||||
# public highlighted message in channel
|
|
||||||
# $0 = highlight color, $1 = nick mode, $2 = nick
|
|
||||||
pubmsghinick = "{msgnick $1 $0$2-%n}";
|
|
||||||
|
|
||||||
# channel name is printed with message
|
|
||||||
msgchannel = "%K:%c$*%n";
|
|
||||||
|
|
||||||
# private message, $0 = nick, $1 = host
|
|
||||||
privmsg = "[%R$0%K(%r$1-%K)%n] ";
|
|
||||||
|
|
||||||
# private message from you, $0 = "msg", $1 = target nick
|
|
||||||
ownprivmsg = "[%r$0%K(%R$1-%K)%n] ";
|
|
||||||
|
|
||||||
# own private message in query
|
|
||||||
ownprivmsgnick = "{msgnick $*}";
|
|
||||||
ownprivnick = "%_$*%n";
|
|
||||||
|
|
||||||
# private message in query
|
|
||||||
privmsgnick = "{msgnick %R$*%n}";
|
|
||||||
|
|
||||||
##
|
|
||||||
## Actions (/ME stuff)
|
|
||||||
##
|
|
||||||
|
|
||||||
# used internally by this theme
|
|
||||||
action_core = "%_ * $*%n";
|
|
||||||
|
|
||||||
# generic one that's used by most actions
|
|
||||||
action = "{action_core $*} ";
|
|
||||||
|
|
||||||
# own action, both private/public
|
|
||||||
ownaction = "{action $*}";
|
|
||||||
|
|
||||||
# own action with target, both private/public
|
|
||||||
ownaction_target = "{action_core $0}%K:%c$1%n ";
|
|
||||||
|
|
||||||
# private action sent by others
|
|
||||||
pvtaction = "%_ (*) $*%n ";
|
|
||||||
pvtaction_query = "{action $*}";
|
|
||||||
|
|
||||||
# public action sent by others
|
|
||||||
pubaction = "{action $*}";
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
## other IRC events
|
|
||||||
##
|
|
||||||
|
|
||||||
# whois
|
|
||||||
whois = "%# $[8]0 : $1-";
|
|
||||||
|
|
||||||
# notices
|
|
||||||
ownnotice = "[%r$0%K(%R$1-%K)]%n ";
|
|
||||||
notice = "%K-%M$*%K-%n ";
|
|
||||||
pubnotice_channel = "%K:%m$*";
|
|
||||||
pvtnotice_host = "%K(%m$*%K)";
|
|
||||||
servernotice = "%g!$*%n ";
|
|
||||||
|
|
||||||
# CTCPs
|
|
||||||
ownctcp = "[%r$0%K(%R$1-%K)] ";
|
|
||||||
ctcp = "%g$*%n";
|
|
||||||
|
|
||||||
# wallops
|
|
||||||
wallop = "%_$*%n: ";
|
|
||||||
wallop_nick = "%n$*";
|
|
||||||
wallop_action = "%_ * $*%n ";
|
|
||||||
|
|
||||||
# netsplits
|
|
||||||
netsplit = "%R$*%n";
|
|
||||||
netjoin = "%C$*%n";
|
|
||||||
|
|
||||||
# /names list
|
|
||||||
names_prefix = "";
|
|
||||||
names_nick = "[%_$0%_$1-] ";
|
|
||||||
names_nick_op = "{names_nick $*}";
|
|
||||||
names_nick_halfop = "{names_nick $*}";
|
|
||||||
names_nick_voice = "{names_nick $*}";
|
|
||||||
names_users = "[%g$*%n]";
|
|
||||||
names_channel = "%G$*%n";
|
|
||||||
|
|
||||||
# DCC
|
|
||||||
dcc = "%g$*%n";
|
|
||||||
dccfile = "%_$*%_";
|
|
||||||
|
|
||||||
# DCC chat, own msg/action
|
|
||||||
dccownmsg = "[%r$0%K($1-%K)%n] ";
|
|
||||||
dccownnick = "%R$*%n";
|
|
||||||
dccownquerynick = "%_$*%n";
|
|
||||||
dccownaction = "{action $*}";
|
|
||||||
dccownaction_target = "{action_core $0}%K:%c$1%n ";
|
|
||||||
|
|
||||||
# DCC chat, others
|
|
||||||
dccmsg = "[%G$1-%K(%g$0%K)%n] ";
|
|
||||||
dccquerynick = "%G$*%n";
|
|
||||||
dccaction = "%_ (*dcc*) $*%n %|";
|
|
||||||
|
|
||||||
##
|
|
||||||
## statusbar
|
|
||||||
##
|
|
||||||
|
|
||||||
# default background for all statusbars. You can also give
|
|
||||||
# the default foreground color for statusbar items.
|
|
||||||
sb_background = "%4%w";
|
|
||||||
|
|
||||||
# default backround for "default" statusbar group
|
|
||||||
#sb_default_bg = "%4";
|
|
||||||
# background for prompt / input line
|
|
||||||
sb_prompt_bg = "%n";
|
|
||||||
# background for info statusbar
|
|
||||||
sb_info_bg = "%8";
|
|
||||||
# background for topicbar (same default)
|
|
||||||
#sb_topic_bg = "%4";
|
|
||||||
|
|
||||||
# text at the beginning of statusbars. sb-item already puts
|
|
||||||
# space there,so we don't use anything by default.
|
|
||||||
sbstart = "";
|
|
||||||
# text at the end of statusbars. Use space so that it's never
|
|
||||||
# used for anything.
|
|
||||||
sbend = " ";
|
|
||||||
|
|
||||||
topicsbstart = "{sbstart $*}";
|
|
||||||
topicsbend = "{sbend $*}";
|
|
||||||
|
|
||||||
prompt = "[$*] ";
|
|
||||||
|
|
||||||
sb = " %c[%n$*%c]%n";
|
|
||||||
sbmode = "(%c+%n$*)";
|
|
||||||
sbaway = " (%GzZzZ%n)";
|
|
||||||
sbservertag = ":$0 (change with ^X)";
|
|
||||||
sbnickmode = "$0";
|
|
||||||
|
|
||||||
# activity in statusbar
|
|
||||||
|
|
||||||
# ',' separator
|
|
||||||
sb_act_sep = "%c$*";
|
|
||||||
# normal text
|
|
||||||
sb_act_text = "%c$*";
|
|
||||||
# public message
|
|
||||||
sb_act_msg = "%W$*";
|
|
||||||
# hilight
|
|
||||||
sb_act_hilight = "%M$*";
|
|
||||||
# hilight with specified color, $0 = color, $1 = text
|
|
||||||
sb_act_hilight_color = "$0$1-%n";
|
|
||||||
};
|
|
||||||
formats = {
|
|
||||||
"Irssi::Script::adv_windowlist" = { awl_display_header = ""; };
|
|
||||||
};
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB
|
|
||||||
lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
|
|
||||||
Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
|
|
||||||
dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
|
|
||||||
SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG
|
|
||||||
A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe
|
|
||||||
MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v
|
|
||||||
d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh
|
|
||||||
cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn
|
|
||||||
0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ
|
|
||||||
M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a
|
|
||||||
MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd
|
|
||||||
oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI
|
|
||||||
DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy
|
|
||||||
oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD
|
|
||||||
VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0
|
|
||||||
dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy
|
|
||||||
bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF
|
|
||||||
BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
|
|
||||||
//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli
|
|
||||||
CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE
|
|
||||||
CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t
|
|
||||||
3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS
|
|
||||||
KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user