Ultima attività 2 weeks ago

Hardened interactive Ubuntu/Debian setup scripts, including CCUsage multi-device sync for Claude Code, Codex CLI, and OpenCode.

Vernon Wee Hong KOH ha revisionato questo gist 1 month ago. Vai alla revisione

2 files changed, 85 insertions, 87 deletions

README.md

@@ -20,32 +20,32 @@ The menu caches `sudo` credentials up-front so multi-task runs don't keep re-pro
20 20
21 21 ## Available scripts
22 22
23 - | # | Script | Runs as | What it does |
24 - |---|---|---|---|
25 - | 1 | [`install-chrome.sh`](install-chrome.sh) | sudo | Installs Google Chrome Stable from Google's official APT repo using a `signed-by` keyring. |
26 - | 2 | [`install-firefox.sh`](install-firefox.sh) | sudo | Removes the Firefox Snap and installs Firefox from Mozilla's official APT repo, with APT pinning so it stays on the Mozilla build. Verifies the Mozilla signing-key fingerprint. |
27 - | 3 | [`install-thunderbird.sh`](install-thunderbird.sh) | sudo | Ubuntu: removes the Snap, adds the Mozilla Team PPA, and pins it. Debian: installs from the standard repos. |
28 - | 4 | [`install-localsend.sh`](install-localsend.sh) | sudo | Installs the latest official LocalSend `.deb` from `localsend/localsend`. SHA-256 verified against the GitHub release asset digest when available. |
29 - | 5 | [`install-telegram.sh`](install-telegram.sh) | sudo | Installs the latest official Telegram Desktop Linux build from Telegram's latest download endpoint. Adds launcher and `tg:` URL handler. |
30 - | 6 | [`install-discord.sh`](install-discord.sh) | sudo | Installs the latest official Discord Linux `.deb` from Discord's latest download endpoint. |
31 - | 7 | [`install-1password.sh`](install-1password.sh) | sudo | Configures the 1Password APT repo with `debsig` signature policy. Arch-aware (amd64 / arm64). |
32 - | 8 | [`install-espanso.sh`](install-espanso.sh) | sudo | Installs Espanso. Auto-detects Wayland vs X11 from `$XDG_SESSION_TYPE` and registers the systemd-user service as the invoking desktop user (not root). |
33 - | 9 | [`install-libreoffice.sh`](install-libreoffice.sh) | sudo | Detects the latest stable release on documentfoundation.org, downloads the matching `.deb` tarball, verifies the published MD5, and installs. Purges the distro's `libreoffice*` first to avoid library conflicts (opt-out with `--keep-distro-libreoffice`). |
34 - | 10 | [`install-obsidian.sh`](install-obsidian.sh) | sudo | Installs the latest official Obsidian amd64 `.deb` from `obsidianmd/obsidian-releases`. SHA-256 verified against the GitHub release asset digest when available. Honors `$GITHUB_TOKEN` to avoid API rate limits. |
35 - | 11 | [`install-drawio.sh`](install-drawio.sh) | sudo | Installs the latest official draw.io Desktop `.deb` from `jgraph/drawio-desktop`. SHA-256 verified against the GitHub release asset digest when available. Honors `$GITHUB_TOKEN` to avoid API rate limits. |
36 - | 12 | [`install-vscode.sh`](install-vscode.sh) | sudo | Microsoft's official `code` APT repo, signed-by keyring. `--insiders` flag installs `code-insiders` instead. |
37 - | 13 | [`install-jetbrains-toolbox.sh`](install-jetbrains-toolbox.sh) | **user** | Per-user install into `~/.local/share/JetBrains/Toolbox`. SHA-256 verified against JetBrains' release feed. x86_64 + aarch64. Drops a `.desktop` launcher. |
38 - | 14 | [`install-bruno.sh`](install-bruno.sh) | sudo | Bruno API client from the official APT repo. Keyserver fetch is wrapped in a 5-attempt retry/backoff because `keyserver.ubuntu.com` is occasionally flaky. |
39 - | 15 | [`install-ipatool.sh`](install-ipatool.sh) | sudo | Installs the latest release of `majd/ipatool` from GitHub. SHA-256 verified against the release `checksums.txt`. Honors `$GITHUB_TOKEN` to avoid API rate limits. |
40 - | 16 | [`install-qbittorrent.sh`](install-qbittorrent.sh) | sudo | Installs the latest official qBittorrent x86_64 AppImage from `qbittorrent/qBittorrent`. SHA-256 verified against the GitHub release asset digest when available. Installs launcher, icons, and torrent/magnet handlers. |
41 - | 17 | [`install-network_drive.sh`](install-network_drive.sh) | sudo | Discovers SMB shares on a Synology NAS and adds them to `/etc/fstab` under `/mnt/Synology` with `x-systemd.automount`. fstab block is managed via begin/end markers so re-runs replace rather than duplicate. Credentials file is `0600`. Best-effort GNOME Dock pin. |
42 - | 18 | [`install-ibus-pinyin.sh`](install-ibus-pinyin.sh) | **user** | Installs `ibus-libpinyin` and Simplified Chinese language packs, restarts the IBus daemon, and idempotently adds `('ibus', 'libpinyin')` to GNOME's input sources via `gsettings`. |
43 - | 19 | [`install-font.sh`](install-font.sh) | **user** | Installs the latest Ubuntu Sans Nerd Font and JetBrains Mono Nerd Font to `~/.local/share/fonts`, then refreshes the font cache. No sudo needed. |
44 - | 20 | [`timedatectl-fix.sh`](timedatectl-fix.sh) | sudo | Sets the hardware clock to UTC to avoid time drift when dual-booting Linux and Windows. |
45 - | 21 | [`install-screenshot-cleanup-cron.sh`](install-screenshot-cleanup-cron.sh) | **user** | Installs an idempotent per-user cron job that clears `$HOME/Pictures/Screenshots` every 5 minutes while preserving the directory itself. |
46 - | 22 | [`disable-grub-countdown.sh`](disable-grub-countdown.sh) | sudo | Configures GRUB to show the dual-boot menu with no countdown by setting `GRUB_TIMEOUT_STYLE=menu` and `GRUB_TIMEOUT=-1`, then regenerates the GRUB config. |
47 - | 23 | [`install-marktext.sh`](install-marktext.sh) | sudo | Installs the latest official MarkText amd64 `.deb` from `marktext/marktext`. SHA-256 verified against the GitHub release asset digest when available. Honors `$GITHUB_TOKEN` to avoid API rate limits. |
48 - | 24 | [`backup-thunderbird-to-synology.sh`](backup-thunderbird-to-synology.sh) | **user** | Backs up `/home/vernon/.thunderbird/` to a configurable Synology NAS destination using `rsync -a --delete`, mountpoint checks, and `flock`. Not included in menu option `0`; run it explicitly. |
23 + | # | Category | Script | Runs as | What it does |
24 + |---|---|---|---|---|
25 + | 1 | Browsers & Mail | [`install-chrome.sh`](install-chrome.sh) | sudo | Installs Google Chrome Stable from Google's official APT repo using a `signed-by` keyring. |
26 + | 2 | Browsers & Mail | [`install-firefox.sh`](install-firefox.sh) | sudo | Removes the Firefox Snap and installs Firefox from Mozilla's official APT repo, with APT pinning so it stays on the Mozilla build. Verifies the Mozilla signing-key fingerprint. |
27 + | 3 | Browsers & Mail | [`install-thunderbird.sh`](install-thunderbird.sh) | sudo | Ubuntu: removes the Snap, adds the Mozilla Team PPA, and pins it. Debian: installs from the standard repos. |
28 + | 4 | Communication | [`install-telegram.sh`](install-telegram.sh) | sudo | Installs the latest official Telegram Desktop Linux build from Telegram's latest download endpoint. Adds launcher and `tg:` URL handler. |
29 + | 5 | Communication | [`install-discord.sh`](install-discord.sh) | sudo | Installs the latest official Discord Linux `.deb` from Discord's latest download endpoint. |
30 + | 6 | Files, Downloads & Storage | [`install-localsend.sh`](install-localsend.sh) | sudo | Installs the latest official LocalSend `.deb` from `localsend/localsend`. SHA-256 verified against the GitHub release asset digest when available. |
31 + | 7 | Files, Downloads & Storage | [`install-qbittorrent.sh`](install-qbittorrent.sh) | sudo | Installs the latest official qBittorrent x86_64 AppImage from `qbittorrent/qBittorrent`. SHA-256 verified against the GitHub release asset digest when available. Installs launcher, icons, and torrent/magnet handlers. |
32 + | 8 | Files, Downloads & Storage | [`install-network_drive.sh`](install-network_drive.sh) | sudo | Discovers SMB shares on a Synology NAS and adds them to `/etc/fstab` under `/mnt/Synology` with `x-systemd.automount`. fstab block is managed via begin/end markers so re-runs replace rather than duplicate. Credentials file is `0600`. Best-effort GNOME Dock pin. |
33 + | 9 | Productivity & Security | [`install-libreoffice.sh`](install-libreoffice.sh) | sudo | Detects the latest stable release on documentfoundation.org, downloads the matching `.deb` tarball, verifies the published MD5, and installs. Purges the distro's `libreoffice*` first to avoid library conflicts (opt-out with `--keep-distro-libreoffice`). |
34 + | 10 | Productivity & Security | [`install-obsidian.sh`](install-obsidian.sh) | sudo | Installs the latest official Obsidian amd64 `.deb` from `obsidianmd/obsidian-releases`. SHA-256 verified against the GitHub release asset digest when available. Honors `$GITHUB_TOKEN` to avoid API rate limits. |
35 + | 11 | Productivity & Security | [`install-marktext.sh`](install-marktext.sh) | sudo | Installs the latest official MarkText amd64 `.deb` from `marktext/marktext`. SHA-256 verified against the GitHub release asset digest when available. Honors `$GITHUB_TOKEN` to avoid API rate limits. |
36 + | 12 | Productivity & Security | [`install-drawio.sh`](install-drawio.sh) | sudo | Installs the latest official draw.io Desktop `.deb` from `jgraph/drawio-desktop`. SHA-256 verified against the GitHub release asset digest when available. Honors `$GITHUB_TOKEN` to avoid API rate limits. |
37 + | 13 | Productivity & Security | [`install-espanso.sh`](install-espanso.sh) | sudo | Installs Espanso. Auto-detects Wayland vs X11 from `$XDG_SESSION_TYPE` and registers the systemd-user service as the invoking desktop user (not root). |
38 + | 14 | Productivity & Security | [`install-1password.sh`](install-1password.sh) | sudo | Configures the 1Password APT repo with `debsig` signature policy. Arch-aware (amd64 / arm64). |
39 + | 15 | Development Tools | [`install-vscode.sh`](install-vscode.sh) | sudo | Microsoft's official `code` APT repo, signed-by keyring. `--insiders` flag installs `code-insiders` instead. |
40 + | 16 | Development Tools | [`install-jetbrains-toolbox.sh`](install-jetbrains-toolbox.sh) | **user** | Per-user install into `~/.local/share/JetBrains/Toolbox`. SHA-256 verified against JetBrains' release feed. x86_64 + aarch64. Drops a `.desktop` launcher. |
41 + | 17 | Development Tools | [`install-bruno.sh`](install-bruno.sh) | sudo | Bruno API client from the official APT repo. Keyserver fetch is wrapped in a 5-attempt retry/backoff because `keyserver.ubuntu.com` is occasionally flaky. |
42 + | 18 | Development Tools | [`install-ipatool.sh`](install-ipatool.sh) | sudo | Installs the latest release of `majd/ipatool` from GitHub. SHA-256 verified against the release `checksums.txt`. Honors `$GITHUB_TOKEN` to avoid API rate limits. |
43 + | 19 | Desktop & Localization | [`install-ibus-pinyin.sh`](install-ibus-pinyin.sh) | **user** | Installs `ibus-libpinyin` and Simplified Chinese language packs, restarts the IBus daemon, and idempotently adds `('ibus', 'libpinyin')` to GNOME's input sources via `gsettings`. |
44 + | 20 | Desktop & Localization | [`install-font.sh`](install-font.sh) | **user** | Installs the latest Ubuntu Sans Nerd Font and JetBrains Mono Nerd Font to `~/.local/share/fonts`, then refreshes the font cache. No sudo needed. |
45 + | 21 | System & Maintenance | [`install-screenshot-cleanup-cron.sh`](install-screenshot-cleanup-cron.sh) | **user** | Installs an idempotent per-user cron job that clears `$HOME/Pictures/Screenshots` every 5 minutes while preserving the directory itself. |
46 + | 22 | System & Maintenance | [`timedatectl-fix.sh`](timedatectl-fix.sh) | sudo | Sets the hardware clock to UTC to avoid time drift when dual-booting Linux and Windows. |
47 + | 23 | System & Maintenance | [`disable-grub-countdown.sh`](disable-grub-countdown.sh) | sudo | Configures GRUB to show the dual-boot menu with no countdown by setting `GRUB_TIMEOUT_STYLE=menu` and `GRUB_TIMEOUT=-1`, then regenerates the GRUB config. |
48 + | 24 | Backup | [`backup-thunderbird-to-synology.sh`](backup-thunderbird-to-synology.sh) | **user** | Backs up `/home/vernon/.thunderbird/` to a configurable Synology NAS destination using `rsync -a --delete`, mountpoint checks, and `flock`. Not included in menu option `0`; run it explicitly. |
49 49
50 50 "Runs as **user**" entries must be invoked as your normal desktop user, not via `sudo`. The other entries elevate via `sudo` internally and the menu primes `sudo -v` up-front, so you'll only be prompted once.
51 51
@@ -54,10 +54,10 @@ The menu caches `sudo` credentials up-front so multi-task runs don't keep re-pro
54 54 If you'd rather skip the menu, each script can be run on its own. Use the right invocation pattern for that script's privilege mode:
55 55
56 56 ```bash
57 - # sudo scripts (1-12, 14-17, 20, 22-23) — pipe through sudo bash
57 + # sudo scripts (1-15, 17-18, 22-23) — pipe through sudo bash
58 58 curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-firefox.sh | sudo bash
59 59
60 - # user scripts (13, 18, 19, 21, 24) — DO NOT use sudo; they install or run per-user
60 + # user scripts (16, 19-21, 24) — DO NOT use sudo; they install or run per-user
61 61 bash -c "$(curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-font.sh)"
62 62 ```
63 63

menu.sh

@@ -103,68 +103,63 @@ run_script() {
103 103 return "$rc"
104 104 }
105 105
106 - # Catalog: number | label | script-filename | mode
106 + # Catalog: number | category | label | script-filename | mode | include-in-all
107 107 # (Edit here to add/remove options — the menu loop is data-driven.)
108 108 OPTIONS=(
109 - "1|Install Google Chrome|install-chrome.sh|sudo"
110 - "2|Install Firefox (Mozilla APT)|install-firefox.sh|sudo"
111 - "3|Install Thunderbird|install-thunderbird.sh|sudo"
112 - "4|Install LocalSend|install-localsend.sh|sudo"
113 - "5|Install Telegram Desktop|install-telegram.sh|sudo"
114 - "6|Install Discord|install-discord.sh|sudo"
115 - "7|Install 1Password|install-1password.sh|sudo"
116 - "8|Install Espanso (text expander)|install-espanso.sh|sudo"
117 - "9|Install LibreOffice (latest stable .deb)|install-libreoffice.sh|sudo"
118 - "10|Install Obsidian|install-obsidian.sh|sudo"
119 - "11|Install draw.io Desktop|install-drawio.sh|sudo"
120 - "12|Install Visual Studio Code|install-vscode.sh|sudo"
121 - "13|Install JetBrains Toolbox (per-user)|install-jetbrains-toolbox.sh|user"
122 - "14|Install Bruno (API client)|install-bruno.sh|sudo"
123 - "15|Install IPATool|install-ipatool.sh|sudo"
124 - "16|Install qBittorrent (latest AppImage)|install-qbittorrent.sh|sudo"
125 - "17|Mount Synology Network Drive|install-network_drive.sh|sudo"
126 - "18|Install IBus Intelligent Pinyin (per-user)|install-ibus-pinyin.sh|user"
127 - "19|Install Nerd Fonts (per-user)|install-font.sh|user"
128 - "20|Fix Dual-Boot Time (RTC to UTC)|timedatectl-fix.sh|sudo"
129 - "21|Install Screenshots Cleanup Cron (per-user)|install-screenshot-cleanup-cron.sh|user"
130 - "22|Disable GRUB Countdown Timer|disable-grub-countdown.sh|sudo"
131 - "23|Install MarkText (Markdown editor)|install-marktext.sh|sudo"
132 - "24|Run Thunderbird Synology Backup (per-user)|backup-thunderbird-to-synology.sh|user"
109 + "1|Browsers & Mail|Install Google Chrome|install-chrome.sh|sudo|yes"
110 + "2|Browsers & Mail|Install Firefox (Mozilla APT)|install-firefox.sh|sudo|yes"
111 + "3|Browsers & Mail|Install Thunderbird|install-thunderbird.sh|sudo|yes"
112 + "4|Communication|Install Telegram Desktop|install-telegram.sh|sudo|yes"
113 + "5|Communication|Install Discord|install-discord.sh|sudo|yes"
114 + "6|Files, Downloads & Storage|Install LocalSend|install-localsend.sh|sudo|yes"
115 + "7|Files, Downloads & Storage|Install qBittorrent (latest AppImage)|install-qbittorrent.sh|sudo|yes"
116 + "8|Files, Downloads & Storage|Mount Synology Network Drive|install-network_drive.sh|sudo|yes"
117 + "9|Productivity & Security|Install LibreOffice (latest stable .deb)|install-libreoffice.sh|sudo|yes"
118 + "10|Productivity & Security|Install Obsidian|install-obsidian.sh|sudo|yes"
119 + "11|Productivity & Security|Install MarkText (Markdown editor)|install-marktext.sh|sudo|yes"
120 + "12|Productivity & Security|Install draw.io Desktop|install-drawio.sh|sudo|yes"
121 + "13|Productivity & Security|Install Espanso (text expander)|install-espanso.sh|sudo|yes"
122 + "14|Productivity & Security|Install 1Password|install-1password.sh|sudo|yes"
123 + "15|Development Tools|Install Visual Studio Code|install-vscode.sh|sudo|yes"
124 + "16|Development Tools|Install JetBrains Toolbox|install-jetbrains-toolbox.sh|user|yes"
125 + "17|Development Tools|Install Bruno (API client)|install-bruno.sh|sudo|yes"
126 + "18|Development Tools|Install IPATool|install-ipatool.sh|sudo|yes"
127 + "19|Desktop & Localization|Install IBus Intelligent Pinyin|install-ibus-pinyin.sh|user|yes"
128 + "20|Desktop & Localization|Install Nerd Fonts|install-font.sh|user|yes"
129 + "21|System & Maintenance|Install Screenshots Cleanup Cron|install-screenshot-cleanup-cron.sh|user|yes"
130 + "22|System & Maintenance|Fix Dual-Boot Time (RTC to UTC)|timedatectl-fix.sh|sudo|yes"
131 + "23|System & Maintenance|Disable GRUB Countdown Timer|disable-grub-countdown.sh|sudo|yes"
132 + "24|Backup|Run Thunderbird Synology Backup|backup-thunderbird-to-synology.sh|user|no"
133 133 )
134 134
135 135 print_menu() {
136 + local entry num category label filename mode include_in_all
137 + local last_category="" note display_label
138 +
136 139 hr
137 140 echo " Ubuntu / Debian Setup Manager"
138 141 hr
139 - echo "--- [ Browsers & Mail ] ---"
140 - echo " 1) Install Google Chrome"
141 - echo " 2) Install Firefox"
142 - echo " 3) Install Thunderbird"
143 - echo "--- [ Communication ] ---"
144 - echo " 4) Install LocalSend"
145 - echo " 5) Install Telegram Desktop"
146 - echo " 6) Install Discord"
147 - echo "--- [ Productivity & Security ] ---"
148 - echo " 7) Install 1Password"
149 - echo " 8) Install Espanso"
150 - echo " 9) Install LibreOffice"
151 - echo " 10) Install Obsidian"
152 - echo " 11) Install draw.io Desktop"
153 - echo "--- [ Development Tools ] ---"
154 - echo " 12) Install Visual Studio Code"
155 - echo " 13) Install JetBrains Toolbox (runs as you, not root)"
156 - echo " 14) Install Bruno"
157 - echo " 15) Install IPATool"
158 - echo " 16) Install qBittorrent"
159 - echo "--- [ System ] ---"
160 - echo " 17) Mount Synology Network Drive"
161 - echo " 18) Install IBus Intelligent Pinyin (runs as you, not root)"
162 - echo " 19) Install Nerd Fonts (runs as you, not root)"
163 - echo " 20) Fix Dual-Boot Time (RTC to UTC)"
164 - echo " 21) Install Screenshots Cleanup Cron (runs as you, not root)"
165 - echo " 22) Disable GRUB Countdown Timer"
166 - echo " 23) Install MarkText"
167 - echo " 24) Run Thunderbird Synology Backup (runs as you, not root; not in ALL)"
142 + for entry in "${OPTIONS[@]}"; do
143 + IFS='|' read -r num category label filename mode include_in_all <<< "$entry"
144 +
145 + if [[ "$category" != "$last_category" ]]; then
146 + printf '%s\n' "--- [ $category ] ---"
147 + last_category="$category"
148 + fi
149 +
150 + note=""
151 + if [[ "$mode" == "user" ]]; then
152 + note="runs as you, not root"
153 + fi
154 + if [[ "$include_in_all" != "yes" ]]; then
155 + [[ -n "$note" ]] && note+="; "
156 + note+="not in ALL"
157 + fi
158 +
159 + display_label="$label"
160 + [[ -n "$note" ]] && display_label+=" ($note)"
161 + printf '%4s) %s\n' "$num" "$display_label"
162 + done
168 163 hr
169 164 echo " 0) Run ALL setup options (1-23)"
170 165 echo " -1) Exit"
@@ -173,19 +168,22 @@ print_menu() {
173 168
174 169 # Resolve a numeric choice to its catalog entry; print "name|mode" to stdout.
175 170 resolve_choice() {
176 - local want="$1" entry num
171 + local want="$1" entry num category label filename mode include_in_all
177 172 for entry in "${OPTIONS[@]}"; do
178 - num="${entry%%|*}"
173 + IFS='|' read -r num category label filename mode include_in_all <<< "$entry"
179 174 if [[ "$num" == "$want" ]]; then
180 - # Strip the leading "N|label|"; what remains is "filename|mode"
181 - printf '%s' "${entry#*|*|}"
175 + printf '%s|%s' "$filename" "$mode"
182 176 return 0
183 177 fi
184 178 done
185 179 return 1
186 180 }
187 181
188 - ALL_NUMS=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23)
182 + ALL_NUMS=()
183 + for entry in "${OPTIONS[@]}"; do
184 + IFS='|' read -r num category label filename mode include_in_all <<< "$entry"
185 + [[ "$include_in_all" == "yes" ]] && ALL_NUMS+=("$num")
186 + done
189 187
190 188 prime_sudo || exit 1
191 189

Vernon Wee Hong KOH ha revisionato questo gist 1 month ago. Vai alla revisione

3 files changed, 124 insertions, 9 deletions

README.md

@@ -13,7 +13,7 @@ bash -c "$(curl -fsSL https://tinyurl.com/bdddmxc4)"
13 13 How to use the picker:
14 14
15 15 - Enter one or more numbers separated by spaces, e.g. `1 6 11`
16 - - `0` runs setup options 1-22 in order
16 + - `0` runs setup options 1-23 in order
17 17 - `-1` exits
18 18
19 19 The menu caches `sudo` credentials up-front so multi-task runs don't keep re-prompting, and falls back to per-user execution for scripts that must not run as root (JetBrains Toolbox, IBus Pinyin, Nerd Fonts, screenshots cleanup, Thunderbird backup).
@@ -44,7 +44,8 @@ The menu caches `sudo` credentials up-front so multi-task runs don't keep re-pro
44 44 | 20 | [`timedatectl-fix.sh`](timedatectl-fix.sh) | sudo | Sets the hardware clock to UTC to avoid time drift when dual-booting Linux and Windows. |
45 45 | 21 | [`install-screenshot-cleanup-cron.sh`](install-screenshot-cleanup-cron.sh) | **user** | Installs an idempotent per-user cron job that clears `$HOME/Pictures/Screenshots` every 5 minutes while preserving the directory itself. |
46 46 | 22 | [`disable-grub-countdown.sh`](disable-grub-countdown.sh) | sudo | Configures GRUB to show the dual-boot menu with no countdown by setting `GRUB_TIMEOUT_STYLE=menu` and `GRUB_TIMEOUT=-1`, then regenerates the GRUB config. |
47 - | 23 | [`backup-thunderbird-to-synology.sh`](backup-thunderbird-to-synology.sh) | **user** | Backs up `/home/vernon/.thunderbird/` to a configurable Synology NAS destination using `rsync -a --delete`, mountpoint checks, and `flock`. Not included in menu option `0`; run it explicitly. |
47 + | 23 | [`install-marktext.sh`](install-marktext.sh) | sudo | Installs the latest official MarkText amd64 `.deb` from `marktext/marktext`. SHA-256 verified against the GitHub release asset digest when available. Honors `$GITHUB_TOKEN` to avoid API rate limits. |
48 + | 24 | [`backup-thunderbird-to-synology.sh`](backup-thunderbird-to-synology.sh) | **user** | Backs up `/home/vernon/.thunderbird/` to a configurable Synology NAS destination using `rsync -a --delete`, mountpoint checks, and `flock`. Not included in menu option `0`; run it explicitly. |
48 49
49 50 "Runs as **user**" entries must be invoked as your normal desktop user, not via `sudo`. The other entries elevate via `sudo` internally and the menu primes `sudo -v` up-front, so you'll only be prompted once.
50 51
@@ -53,10 +54,10 @@ The menu caches `sudo` credentials up-front so multi-task runs don't keep re-pro
53 54 If you'd rather skip the menu, each script can be run on its own. Use the right invocation pattern for that script's privilege mode:
54 55
55 56 ```bash
56 - # sudo scripts (1-12, 14-17, 20, 22) — pipe through sudo bash
57 + # sudo scripts (1-12, 14-17, 20, 22-23) — pipe through sudo bash
57 58 curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-firefox.sh | sudo bash
58 59
59 - # user scripts (13, 18, 19, 21, 23) — DO NOT use sudo; they install or run per-user
60 + # user scripts (13, 18, 19, 21, 24) — DO NOT use sudo; they install or run per-user
60 61 bash -c "$(curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-font.sh)"
61 62 ```
62 63
@@ -112,7 +113,7 @@ The hardened installers generally share this robustness baseline:
112 113 - `dpkg --print-architecture` / `uname -m` for architecture (amd64 / arm64 / armhf / x86_64 / aarch64 as applicable to each upstream)
113 114 - Idempotent — re-running a script does not duplicate APT sources, fstab entries, gsettings entries, or `.desktop` files
114 115 - `signed-by` keyrings in `/etc/apt/keyrings` (no deprecated `apt-key add`)
115 - - Checksum verification where the upstream publishes one (JetBrains SHA-256, LibreOffice MD5, Obsidian SHA-256, ipatool SHA-256, draw.io SHA-256, qBittorrent SHA-256, LocalSend SHA-256)
116 + - Checksum verification where the upstream publishes one (JetBrains SHA-256, LibreOffice MD5, Obsidian SHA-256, MarkText SHA-256, ipatool SHA-256, draw.io SHA-256, qBittorrent SHA-256, LocalSend SHA-256)
116 117 - Per-user installers refuse to run as root; system installers refuse to run as non-root
117 118
118 119 ## Hide Drive from the Dock

install-marktext.sh(file creato)

@@ -0,0 +1,112 @@
1 + #!/usr/bin/env bash
2 + # install-marktext.sh — Install MarkText from the latest official GitHub release.
3 + # Hardened: architecture check, GitHub API token support, SHA-256 verification
4 + # from the release asset digest, idempotent, --dry-run.
5 +
6 + set -euo pipefail
7 + IFS=$'\n\t'
8 +
9 + readonly SCRIPT_NAME="${0##*/}"
10 + DRY_RUN=0
11 + REPO="marktext/marktext"
12 +
13 + usage() {
14 + cat <<EOF
15 + Usage: sudo $SCRIPT_NAME [--dry-run] [--help]
16 +
17 + Resolves the latest stable release of marktext/marktext, downloads the official
18 + Linux amd64 .deb package, verifies its SHA-256 against the GitHub release asset
19 + digest, and installs it with apt-get.
20 +
21 + If \$GITHUB_TOKEN is set in the environment, it is used to authenticate the
22 + GitHub API request (avoids rate limits).
23 +
24 + Options:
25 + --dry-run Print actions without executing.
26 + --help, -h Show this help.
27 + EOF
28 + }
29 +
30 + log() { printf '\033[1;34m[%s]\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*"; }
31 + warn() { printf '\033[1;33m[%s] WARN:\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*" >&2; }
32 + die() { printf '\033[1;31m[%s] ERROR:\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*" >&2; exit 1; }
33 + run() { if (( DRY_RUN )); then printf ' DRY-RUN: %s\n' "$*"; else eval "$@"; fi; }
34 + trap 'rc=$?; (( rc )) && printf "\033[1;31m[%s] failed at line %s (exit %d)\033[0m\n" "${SCRIPT_NAME%.sh}" "$LINENO" "$rc" >&2' ERR
35 +
36 + while (( $# )); do
37 + case "$1" in
38 + --dry-run) DRY_RUN=1 ;;
39 + -h|--help) usage; exit 0 ;;
40 + *) die "Unknown argument: $1 (try --help)" ;;
41 + esac
42 + shift
43 + done
44 +
45 + (( EUID == 0 )) || die "Must run as root. Try: sudo $SCRIPT_NAME"
46 +
47 + [[ -r /etc/os-release ]] || die "/etc/os-release not found."
48 + # shellcheck disable=SC1091
49 + . /etc/os-release
50 + case "${ID:-}:${ID_LIKE:-}" in
51 + *ubuntu*|*debian*) : ;;
52 + *) die "Unsupported distro: ${PRETTY_NAME:-unknown}." ;;
53 + esac
54 +
55 + ARCH="$(dpkg --print-architecture)"
56 + [[ "$ARCH" == "amd64" ]] || die "MarkText publishes its Linux .deb for amd64 only (detected: $ARCH)."
57 + log "Detected: ${PRETTY_NAME:-unknown}, arch: $ARCH"
58 +
59 + export DEBIAN_FRONTEND=noninteractive
60 + log "Installing prerequisites..."
61 + run "apt-get update -qq"
62 + run "apt-get install -y curl jq ca-certificates"
63 +
64 + GH_HDRS=(-H "Accept: application/vnd.github+json")
65 + [[ -n "${GITHUB_TOKEN:-}" ]] && GH_HDRS+=(-H "Authorization: Bearer ${GITHUB_TOKEN}")
66 +
67 + log "Querying GitHub API for latest release of $REPO..."
68 + RELEASE_JSON="$(curl -fsSL "${GH_HDRS[@]}" "https://api.github.com/repos/${REPO}/releases/latest")"
69 + TAG="$(jq -r '.tag_name // empty' <<<"$RELEASE_JSON")"
70 + [[ -n "$TAG" ]] || die "Could not parse latest release tag (rate limited? set GITHUB_TOKEN)."
71 + log "Latest release: $TAG"
72 +
73 + ASSET_REGEX='^marktext-linux-[^/]+\.deb$'
74 + DOWNLOAD_URL="$(jq -r --arg re "$ASSET_REGEX" '.assets[] | select(.name | test($re)) | .browser_download_url' <<<"$RELEASE_JSON" | head -n1)"
75 + ASSET_NAME="$(jq -r --arg re "$ASSET_REGEX" '.assets[] | select(.name | test($re)) | .name' <<<"$RELEASE_JSON" | head -n1)"
76 + EXPECTED_SHA="$(jq -r --arg re "$ASSET_REGEX" '.assets[] | select(.name | test($re)) | (.digest // "") | sub("^sha256:"; "")' <<<"$RELEASE_JSON" | head -n1)"
77 + [[ "$DOWNLOAD_URL" =~ ^https:// ]] || die "No amd64 .deb asset found in latest MarkText release."
78 +
79 + STAGE="$(mktemp -d -t marktext.XXXXXX)"
80 + trap 'rm -rf "$STAGE"' EXIT
81 + DEB="$STAGE/$ASSET_NAME"
82 +
83 + log "Downloading $ASSET_NAME..."
84 + run "curl -fsSL -o '$DEB' '$DOWNLOAD_URL'"
85 +
86 + if (( DRY_RUN )); then
87 + [[ -n "$EXPECTED_SHA" && "$EXPECTED_SHA" != "null" ]] \
88 + && printf ' DRY-RUN: verify SHA-256 %s\n' "$EXPECTED_SHA" \
89 + || printf ' DRY-RUN: skip SHA-256 verification; no GitHub asset digest found\n'
90 + printf ' DRY-RUN: apt-get install -y %s\n' "$DEB"
91 + log "Done."
92 + exit 0
93 + fi
94 +
95 + if [[ -n "$EXPECTED_SHA" && "$EXPECTED_SHA" != "null" ]]; then
96 + log "Verifying SHA-256..."
97 + ACTUAL_SHA="$(sha256sum "$DEB" | awk '{print $1}')"
98 + [[ "$EXPECTED_SHA" == "$ACTUAL_SHA" ]] || die "SHA-256 mismatch: expected=$EXPECTED_SHA actual=$ACTUAL_SHA"
99 + log "SHA-256 ok."
100 + else
101 + warn "No SHA-256 asset digest in GitHub release metadata; skipping verification."
102 + fi
103 +
104 + log "Installing MarkText..."
105 + run "apt-get install -y '$DEB'"
106 +
107 + if command -v marktext >/dev/null 2>&1; then
108 + log "Installed: $(marktext --version 2>/dev/null || echo "$TAG")"
109 + else
110 + log "Installed: $TAG"
111 + fi
112 + log "Done."

menu.sh

@@ -128,7 +128,8 @@ OPTIONS=(
128 128 "20|Fix Dual-Boot Time (RTC to UTC)|timedatectl-fix.sh|sudo"
129 129 "21|Install Screenshots Cleanup Cron (per-user)|install-screenshot-cleanup-cron.sh|user"
130 130 "22|Disable GRUB Countdown Timer|disable-grub-countdown.sh|sudo"
131 - "23|Run Thunderbird Synology Backup (per-user)|backup-thunderbird-to-synology.sh|user"
131 + "23|Install MarkText (Markdown editor)|install-marktext.sh|sudo"
132 + "24|Run Thunderbird Synology Backup (per-user)|backup-thunderbird-to-synology.sh|user"
132 133 )
133 134
134 135 print_menu() {
@@ -162,9 +163,10 @@ print_menu() {
162 163 echo " 20) Fix Dual-Boot Time (RTC to UTC)"
163 164 echo " 21) Install Screenshots Cleanup Cron (runs as you, not root)"
164 165 echo " 22) Disable GRUB Countdown Timer"
165 - echo " 23) Run Thunderbird Synology Backup (runs as you, not root; not in ALL)"
166 + echo " 23) Install MarkText"
167 + echo " 24) Run Thunderbird Synology Backup (runs as you, not root; not in ALL)"
166 168 hr
167 - echo " 0) Run ALL setup options (1-22)"
169 + echo " 0) Run ALL setup options (1-23)"
168 170 echo " -1) Exit"
169 171 hr
170 172 }
@@ -183,7 +185,7 @@ resolve_choice() {
183 185 return 1
184 186 }
185 187
186 - ALL_NUMS=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22)
188 + ALL_NUMS=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23)
187 189
188 190 prime_sudo || exit 1
189 191

Vernon Wee Hong KOH ha revisionato questo gist 1 month ago. Vai alla revisione

5 files changed, 322 insertions, 6 deletions

README.md

@@ -1,6 +1,6 @@
1 1 # Ubuntu / Debian Setup Manager
2 2
3 - A collection of hardened install scripts for a fresh Ubuntu / Debian desktop, driven by an interactive `menu.sh`. Each script auto-detects the distro, uses `signed-by` APT keyrings where applicable (no `apt-key`), and is idempotent (safe to re-run).
3 + A collection of hardened install and maintenance scripts for a fresh Ubuntu / Debian desktop, driven by an interactive `menu.sh`. Each installer auto-detects the distro, uses `signed-by` APT keyrings where applicable (no `apt-key`), and is idempotent (safe to re-run).
4 4
5 5 ## Quick start — run the menu
6 6
@@ -13,10 +13,10 @@ bash -c "$(curl -fsSL https://tinyurl.com/bdddmxc4)"
13 13 How to use the picker:
14 14
15 15 - Enter one or more numbers separated by spaces, e.g. `1 6 11`
16 - - `0` runs every option in order
16 + - `0` runs setup options 1-22 in order
17 17 - `-1` exits
18 18
19 - The menu caches `sudo` credentials up-front so multi-task runs don't keep re-prompting, and falls back to per-user execution for the three scripts that must not run as root (JetBrains Toolbox, IBus Pinyin, Nerd Fonts).
19 + The menu caches `sudo` credentials up-front so multi-task runs don't keep re-prompting, and falls back to per-user execution for scripts that must not run as root (JetBrains Toolbox, IBus Pinyin, Nerd Fonts, screenshots cleanup, Thunderbird backup).
20 20
21 21 ## Available scripts
22 22
@@ -44,6 +44,7 @@ The menu caches `sudo` credentials up-front so multi-task runs don't keep re-pro
44 44 | 20 | [`timedatectl-fix.sh`](timedatectl-fix.sh) | sudo | Sets the hardware clock to UTC to avoid time drift when dual-booting Linux and Windows. |
45 45 | 21 | [`install-screenshot-cleanup-cron.sh`](install-screenshot-cleanup-cron.sh) | **user** | Installs an idempotent per-user cron job that clears `$HOME/Pictures/Screenshots` every 5 minutes while preserving the directory itself. |
46 46 | 22 | [`disable-grub-countdown.sh`](disable-grub-countdown.sh) | sudo | Configures GRUB to show the dual-boot menu with no countdown by setting `GRUB_TIMEOUT_STYLE=menu` and `GRUB_TIMEOUT=-1`, then regenerates the GRUB config. |
47 + | 23 | [`backup-thunderbird-to-synology.sh`](backup-thunderbird-to-synology.sh) | **user** | Backs up `/home/vernon/.thunderbird/` to a configurable Synology NAS destination using `rsync -a --delete`, mountpoint checks, and `flock`. Not included in menu option `0`; run it explicitly. |
47 48
48 49 "Runs as **user**" entries must be invoked as your normal desktop user, not via `sudo`. The other entries elevate via `sudo` internally and the menu primes `sudo -v` up-front, so you'll only be prompted once.
49 50
@@ -55,17 +56,53 @@ If you'd rather skip the menu, each script can be run on its own. Use the right
55 56 # sudo scripts (1-12, 14-17, 20, 22) — pipe through sudo bash
56 57 curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-firefox.sh | sudo bash
57 58
58 - # user scripts (13, 18, 19, 21) — DO NOT use sudo; they install per-user
59 + # user scripts (13, 18, 19, 21, 23) — DO NOT use sudo; they install or run per-user
59 60 bash -c "$(curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-font.sh)"
60 61 ```
61 62
62 - Most installers accept `--help` and `--dry-run` (the latter prints what would happen without executing). For example:
63 + Most installers and utilities accept `--help` and `--dry-run` (the latter prints what would happen without executing). For example:
63 64
64 65 ```bash
65 66 curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-libreoffice.sh \
66 67 | bash -s -- --help
67 68 ```
68 69
70 + ## Thunderbird to Synology Backup
71 +
72 + The Thunderbird backup utility reads its default config from this gist:
73 +
74 + ```bash
75 + https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/thunderbird-backup.conf
76 + ```
77 +
78 + Run it manually:
79 +
80 + ```bash
81 + bash -c "$(curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/backup-thunderbird-to-synology.sh)"
82 + ```
83 +
84 + Preview changes without writing files:
85 +
86 + ```bash
87 + curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/backup-thunderbird-to-synology.sh \
88 + | bash -s -- --dry-run
89 + ```
90 +
91 + To change the backup location for every machine using the hosted config, edit `thunderbird-backup.conf` in this gist and update `DESTINATION`; if the backup location is on a different mounted drive or NAS share, update `MOUNTPOINT` too. The script parses only `SOURCE`, `DESTINATION`, `MOUNTPOINT`, `LOG_FILE`, and `LOCK_FILE`; unsupported keys or invalid lines stop the run.
92 +
93 + For hourly backups, install the script locally and add it to the current user's crontab:
94 +
95 + ```bash
96 + mkdir -p /home/vernon/bin
97 + curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/backup-thunderbird-to-synology.sh \
98 + -o /home/vernon/bin/backup-thunderbird-to-synology.sh
99 + chmod 755 /home/vernon/bin/backup-thunderbird-to-synology.sh
100 + ```
101 +
102 + ```cron
103 + 0 * * * * /home/vernon/bin/backup-thunderbird-to-synology.sh
104 + ```
105 +
69 106 ## What "hardened" means here
70 107
71 108 The hardened installers generally share this robustness baseline:

backup-thunderbird-to-synology.sh(file creato)

@@ -0,0 +1,258 @@
1 + #!/usr/bin/env bash
2 + set -euo pipefail
3 +
4 + CONFIG_URL_DEFAULT="https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/thunderbird-backup.conf"
5 + CONFIG_FILE="${THUNDERBIRD_BACKUP_CONFIG:-}"
6 + CONFIG_URL="${THUNDERBIRD_BACKUP_CONFIG_URL:-$CONFIG_URL_DEFAULT}"
7 + SHOW_HELP=false
8 +
9 + args=("$@")
10 + for ((i = 0; i < ${#args[@]}; i++)); do
11 + case "${args[$i]}" in
12 + --config)
13 + CONFIG_FILE="${args[$((i + 1))]:?Missing value for --config}"
14 + CONFIG_URL=""
15 + ;;
16 + --config-url)
17 + CONFIG_URL="${args[$((i + 1))]:?Missing value for --config-url}"
18 + CONFIG_FILE=""
19 + ;;
20 + -h|--help)
21 + SHOW_HELP=true
22 + ;;
23 + esac
24 + done
25 +
26 + SOURCE_DEFAULT="/home/vernon/.thunderbird/"
27 + DESTINATION_DEFAULT="/mnt/Synology/Backup/Applications/Thunderbird/"
28 + MOUNTPOINT_DEFAULT="/mnt/Synology/Backup"
29 + LOG_FILE_DEFAULT="/home/vernon/.local/state/thunderbird-backup.log"
30 + LOCK_FILE_DEFAULT="/home/vernon/.cache/thunderbird-backup.lock"
31 + DRY_RUN=false
32 +
33 + SOURCE="$SOURCE_DEFAULT"
34 + DESTINATION="$DESTINATION_DEFAULT"
35 + MOUNTPOINT="$MOUNTPOINT_DEFAULT"
36 + LOG_FILE="$LOG_FILE_DEFAULT"
37 + LOCK_FILE="$LOCK_FILE_DEFAULT"
38 +
39 + trim() {
40 + local value="$1"
41 + value="${value#"${value%%[![:space:]]*}"}"
42 + value="${value%"${value##*[![:space:]]}"}"
43 + printf '%s' "$value"
44 + }
45 +
46 + parse_config_content() {
47 + local line key raw value
48 +
49 + while IFS= read -r line || [[ -n "$line" ]]; do
50 + line="$(trim "$line")"
51 + [[ -z "$line" || "$line" == \#* ]] && continue
52 +
53 + if [[ ! "$line" =~ ^([A-Z_]+)[[:space:]]*=(.*)$ ]]; then
54 + echo "ERROR: Invalid config line: $line" >&2
55 + exit 2
56 + fi
57 +
58 + key="${BASH_REMATCH[1]}"
59 + raw="$(trim "${BASH_REMATCH[2]}")"
60 +
61 + if [[ "$raw" == \"*\" && "$raw" == *\" ]]; then
62 + value="${raw:1:${#raw}-2}"
63 + elif [[ "$raw" == \'*\' && "$raw" == *\' ]]; then
64 + value="${raw:1:${#raw}-2}"
65 + else
66 + value="$raw"
67 + fi
68 +
69 + case "$key" in
70 + SOURCE) SOURCE="$value" ;;
71 + DESTINATION) DESTINATION="$value" ;;
72 + MOUNTPOINT) MOUNTPOINT="$value" ;;
73 + LOG_FILE) LOG_FILE="$value" ;;
74 + LOCK_FILE) LOCK_FILE="$value" ;;
75 + *)
76 + echo "ERROR: Unsupported config key: $key" >&2
77 + exit 2
78 + ;;
79 + esac
80 + done
81 + }
82 +
83 + usage() {
84 + cat <<'USAGE'
85 + Usage: backup-thunderbird-to-synology.sh [options]
86 +
87 + Options:
88 + --source PATH Thunderbird profile directory to back up
89 + --destination PATH Backup destination directory
90 + --mountpoint PATH Required mounted path to check before backup
91 + --no-mount-check Disable the mountpoint safety check
92 + --log-file PATH Log file path
93 + --config PATH Local config file path; disables remote config
94 + --config-url URL Remote config URL
95 + --dry-run Show what would change without writing files
96 + -h, --help Show this help
97 +
98 + Config defaults to:
99 + https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/thunderbird-backup.conf
100 +
101 + Supported config variables: SOURCE, DESTINATION, MOUNTPOINT, LOG_FILE, LOCK_FILE.
102 + Environment overrides: THUNDERBIRD_BACKUP_SOURCE, THUNDERBIRD_BACKUP_DESTINATION,
103 + THUNDERBIRD_BACKUP_MOUNTPOINT, THUNDERBIRD_BACKUP_LOG_FILE, THUNDERBIRD_BACKUP_LOCK_FILE,
104 + THUNDERBIRD_BACKUP_CONFIG, THUNDERBIRD_BACKUP_CONFIG_URL.
105 + USAGE
106 + }
107 +
108 + if [[ "$SHOW_HELP" == true ]]; then
109 + usage
110 + exit 0
111 + fi
112 +
113 + if [[ -n "$CONFIG_FILE" ]]; then
114 + if [[ ! -f "$CONFIG_FILE" ]]; then
115 + mkdir -p "$(dirname "$LOG_FILE")"
116 + echo "$(date -Is) ERROR: Config file not found: $CONFIG_FILE" >> "$LOG_FILE"
117 + exit 1
118 + fi
119 + parse_config_content < "$CONFIG_FILE"
120 + elif [[ -n "$CONFIG_URL" ]]; then
121 + config_content="$(curl -fsSL --max-time 20 "$CONFIG_URL")" || {
122 + mkdir -p "$(dirname "$LOG_FILE")"
123 + echo "$(date -Is) ERROR: Unable to fetch config from $CONFIG_URL" >> "$LOG_FILE"
124 + exit 1
125 + }
126 + parse_config_content <<< "$config_content"
127 + fi
128 +
129 + SOURCE="${THUNDERBIRD_BACKUP_SOURCE:-$SOURCE}"
130 + DESTINATION="${THUNDERBIRD_BACKUP_DESTINATION:-$DESTINATION}"
131 + MOUNTPOINT="${THUNDERBIRD_BACKUP_MOUNTPOINT:-$MOUNTPOINT}"
132 + LOG_FILE="${THUNDERBIRD_BACKUP_LOG_FILE:-$LOG_FILE}"
133 + LOCK_FILE="${THUNDERBIRD_BACKUP_LOCK_FILE:-$LOCK_FILE}"
134 +
135 + while [[ $# -gt 0 ]]; do
136 + case "$1" in
137 + --source)
138 + SOURCE="${2:?Missing value for --source}"
139 + shift 2
140 + ;;
141 + --destination)
142 + DESTINATION="${2:?Missing value for --destination}"
143 + shift 2
144 + ;;
145 + --mountpoint)
146 + MOUNTPOINT="${2:?Missing value for --mountpoint}"
147 + shift 2
148 + ;;
149 + --no-mount-check)
150 + MOUNTPOINT=""
151 + shift
152 + ;;
153 + --log-file)
154 + LOG_FILE="${2:?Missing value for --log-file}"
155 + shift 2
156 + ;;
157 + --dry-run)
158 + DRY_RUN=true
159 + shift
160 + ;;
161 + --config)
162 + shift 2
163 + ;;
164 + --config-url)
165 + shift 2
166 + ;;
167 + -h|--help)
168 + usage
169 + exit 0
170 + ;;
171 + *)
172 + echo "ERROR: Unknown option: $1" >&2
173 + usage >&2
174 + exit 2
175 + ;;
176 + esac
177 + done
178 +
179 + mkdir -p "$(dirname "$LOG_FILE")" "$(dirname "$LOCK_FILE")"
180 +
181 + timestamp() {
182 + date -Is
183 + }
184 +
185 + normalize_path() {
186 + realpath -m "$1"
187 + }
188 +
189 + SOURCE_REAL="$(normalize_path "$SOURCE")"
190 + DESTINATION_REAL="$(normalize_path "$DESTINATION")"
191 +
192 + if [[ "$SOURCE" != /* || "$DESTINATION" != /* || "$LOG_FILE" != /* || "$LOCK_FILE" != /* ]]; then
193 + echo "$(timestamp) ERROR: SOURCE, DESTINATION, LOG_FILE, and LOCK_FILE must be absolute paths." >> "$LOG_FILE"
194 + exit 1
195 + fi
196 +
197 + if [[ -n "$MOUNTPOINT" && "$MOUNTPOINT" != /* ]]; then
198 + echo "$(timestamp) ERROR: MOUNTPOINT must be an absolute path." >> "$LOG_FILE"
199 + exit 1
200 + fi
201 +
202 + if [[ "$SOURCE_REAL" == "$DESTINATION_REAL" ]]; then
203 + echo "$(timestamp) ERROR: SOURCE and DESTINATION cannot be the same path: $SOURCE_REAL" >> "$LOG_FILE"
204 + exit 1
205 + fi
206 +
207 + if [[ "$DESTINATION_REAL" == "$SOURCE_REAL"/* ]]; then
208 + echo "$(timestamp) ERROR: DESTINATION cannot be inside SOURCE: $DESTINATION_REAL" >> "$LOG_FILE"
209 + exit 1
210 + fi
211 +
212 + case "$DESTINATION_REAL" in
213 + /|/home|/home/vernon|/mnt|/mnt/Synology|/mnt/Synology/Backup)
214 + echo "$(timestamp) ERROR: Refusing unsafe backup destination: $DESTINATION_REAL" >> "$LOG_FILE"
215 + exit 1
216 + ;;
217 + esac
218 +
219 + if [[ -n "$MOUNTPOINT" ]] && ! mountpoint -q "$MOUNTPOINT"; then
220 + echo "$(timestamp) ERROR: $MOUNTPOINT is not mounted; backup skipped." >> "$LOG_FILE"
221 + exit 1
222 + fi
223 +
224 + if [[ ! -d "$SOURCE" ]]; then
225 + echo "$(timestamp) ERROR: Thunderbird profile directory not found: $SOURCE" >> "$LOG_FILE"
226 + exit 1
227 + fi
228 +
229 + mkdir -p "$DESTINATION"
230 +
231 + exec 9>"$LOCK_FILE"
232 + if ! flock -n 9; then
233 + echo "$(timestamp) INFO: Previous Thunderbird backup still running; backup skipped." >> "$LOG_FILE"
234 + exit 0
235 + fi
236 +
237 + {
238 + if [[ "$DRY_RUN" == true ]]; then
239 + echo "$(timestamp) INFO: Starting Thunderbird backup dry run: $SOURCE_REAL -> $DESTINATION_REAL"
240 + else
241 + echo "$(timestamp) INFO: Starting Thunderbird backup: $SOURCE_REAL -> $DESTINATION_REAL"
242 + fi
243 +
244 + rsync_args=(-a --delete)
245 + if [[ "$DRY_RUN" == true ]]; then
246 + rsync_args+=(-n --itemize-changes)
247 + fi
248 +
249 + rsync "${rsync_args[@]}" \
250 + --exclude='*.parentlock' \
251 + --exclude='lock' \
252 + "$SOURCE" "$DESTINATION"
253 + if [[ "$DRY_RUN" == true ]]; then
254 + echo "$(timestamp) INFO: Thunderbird backup dry run completed."
255 + else
256 + echo "$(timestamp) INFO: Thunderbird backup completed."
257 + fi
258 + } >> "$LOG_FILE" 2>&1

menu.sh

@@ -128,6 +128,7 @@ OPTIONS=(
128 128 "20|Fix Dual-Boot Time (RTC to UTC)|timedatectl-fix.sh|sudo"
129 129 "21|Install Screenshots Cleanup Cron (per-user)|install-screenshot-cleanup-cron.sh|user"
130 130 "22|Disable GRUB Countdown Timer|disable-grub-countdown.sh|sudo"
131 + "23|Run Thunderbird Synology Backup (per-user)|backup-thunderbird-to-synology.sh|user"
131 132 )
132 133
133 134 print_menu() {
@@ -161,8 +162,9 @@ print_menu() {
161 162 echo " 20) Fix Dual-Boot Time (RTC to UTC)"
162 163 echo " 21) Install Screenshots Cleanup Cron (runs as you, not root)"
163 164 echo " 22) Disable GRUB Countdown Timer"
165 + echo " 23) Run Thunderbird Synology Backup (runs as you, not root; not in ALL)"
164 166 hr
165 - echo " 0) Run ALL options (1-22)"
167 + echo " 0) Run ALL setup options (1-22)"
166 168 echo " -1) Exit"
167 169 hr
168 170 }

thunderbird-backup.conf(file creato)

@@ -0,0 +1,9 @@
1 + # Thunderbird backup settings.
2 + # This file is fetched by backup-thunderbird-to-synology.sh from OpenGist.
3 + # All paths should be absolute.
4 +
5 + SOURCE="/home/vernon/.thunderbird/"
6 + DESTINATION="/mnt/Synology/Backup/Applications/Thunderbird/"
7 + MOUNTPOINT="/mnt/Synology/Backup"
8 + LOG_FILE="/home/vernon/.local/state/thunderbird-backup.log"
9 + LOCK_FILE="/home/vernon/.cache/thunderbird-backup.lock"

thunderbird-backup.conf.example(file creato)

@@ -0,0 +1,10 @@
1 + # Thunderbird backup settings.
2 + # Use this as a local override with:
3 + # backup-thunderbird-to-synology.sh --config /path/to/thunderbird-backup.conf
4 + # All paths should be absolute.
5 +
6 + SOURCE="/home/vernon/.thunderbird/"
7 + DESTINATION="/mnt/Synology/Backup/Applications/Thunderbird/"
8 + MOUNTPOINT="/mnt/Synology/Backup"
9 + LOG_FILE="/home/vernon/.local/state/thunderbird-backup.log"
10 + LOCK_FILE="/home/vernon/.cache/thunderbird-backup.lock"

Vernon Wee Hong KOH ha revisionato questo gist 1 month ago. Vai alla revisione

3 files changed, 99 insertions, 3 deletions

README.md

@@ -43,6 +43,7 @@ The menu caches `sudo` credentials up-front so multi-task runs don't keep re-pro
43 43 | 19 | [`install-font.sh`](install-font.sh) | **user** | Installs the latest Ubuntu Sans Nerd Font and JetBrains Mono Nerd Font to `~/.local/share/fonts`, then refreshes the font cache. No sudo needed. |
44 44 | 20 | [`timedatectl-fix.sh`](timedatectl-fix.sh) | sudo | Sets the hardware clock to UTC to avoid time drift when dual-booting Linux and Windows. |
45 45 | 21 | [`install-screenshot-cleanup-cron.sh`](install-screenshot-cleanup-cron.sh) | **user** | Installs an idempotent per-user cron job that clears `$HOME/Pictures/Screenshots` every 5 minutes while preserving the directory itself. |
46 + | 22 | [`disable-grub-countdown.sh`](disable-grub-countdown.sh) | sudo | Configures GRUB to show the dual-boot menu with no countdown by setting `GRUB_TIMEOUT_STYLE=menu` and `GRUB_TIMEOUT=-1`, then regenerates the GRUB config. |
46 47
47 48 "Runs as **user**" entries must be invoked as your normal desktop user, not via `sudo`. The other entries elevate via `sudo` internally and the menu primes `sudo -v` up-front, so you'll only be prompted once.
48 49
@@ -51,7 +52,7 @@ The menu caches `sudo` credentials up-front so multi-task runs don't keep re-pro
51 52 If you'd rather skip the menu, each script can be run on its own. Use the right invocation pattern for that script's privilege mode:
52 53
53 54 ```bash
54 - # sudo scripts (1-12, 14-17, 20) — pipe through sudo bash
55 + # sudo scripts (1-12, 14-17, 20, 22) — pipe through sudo bash
55 56 curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-firefox.sh | sudo bash
56 57
57 58 # user scripts (13, 18, 19, 21) — DO NOT use sudo; they install per-user

disable-grub-countdown.sh(file creato)

@@ -0,0 +1,93 @@
1 + #!/usr/bin/env bash
2 + # disable-grub-countdown.sh — Show the GRUB dual-boot menu without a countdown.
3 +
4 + set -euo pipefail
5 + IFS=$'\n\t'
6 +
7 + readonly SCRIPT_NAME="${0##*/}"
8 + readonly GRUB_DEFAULTS="/etc/default/grub"
9 + DRY_RUN=0
10 +
11 + usage() {
12 + cat <<EOF
13 + Usage: $SCRIPT_NAME [--dry-run] [--help]
14 +
15 + Configures GRUB to show the boot menu and wait indefinitely:
16 +
17 + GRUB_TIMEOUT_STYLE=menu
18 + GRUB_TIMEOUT=-1
19 +
20 + Options:
21 + --dry-run Print actions without executing.
22 + --help, -h Show this help.
23 + EOF
24 + }
25 +
26 + log() { printf '\033[1;34m[%s]\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*"; }
27 + die() { printf '\033[1;31m[%s] ERROR:\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*" >&2; exit 1; }
28 +
29 + while (( $# )); do
30 + case "$1" in
31 + --dry-run) DRY_RUN=1 ;;
32 + -h|--help) usage; exit 0 ;;
33 + *) die "Unknown argument: $1 (try --help)" ;;
34 + esac
35 + shift
36 + done
37 +
38 + (( EUID == 0 )) || die "Please run as root: sudo bash $SCRIPT_NAME"
39 + [[ -f "$GRUB_DEFAULTS" ]] || die "Missing $GRUB_DEFAULTS. This system does not look like a GRUB-based Ubuntu/Debian install."
40 +
41 + for tool in cp date grep mktemp sed; do
42 + command -v "$tool" >/dev/null 2>&1 || die "Missing required tool: $tool"
43 + done
44 +
45 + if command -v update-grub >/dev/null 2>&1; then
46 + GRUB_UPDATE_CMD=(update-grub)
47 + elif command -v grub-mkconfig >/dev/null 2>&1; then
48 + GRUB_UPDATE_CMD=(grub-mkconfig -o /boot/grub/grub.cfg)
49 + elif command -v grub2-mkconfig >/dev/null 2>&1; then
50 + if [[ -d /boot/grub2 ]]; then
51 + GRUB_UPDATE_CMD=(grub2-mkconfig -o /boot/grub2/grub.cfg)
52 + else
53 + GRUB_UPDATE_CMD=(grub2-mkconfig -o /boot/grub/grub.cfg)
54 + fi
55 + else
56 + die "Could not find update-grub, grub-mkconfig, or grub2-mkconfig."
57 + fi
58 +
59 + BACKUP="${GRUB_DEFAULTS}.backup.$(date +%Y%m%d-%H%M%S)"
60 + STAGE="$(mktemp -t grub-defaults.XXXXXX)"
61 + trap 'rm -f "$STAGE"' EXIT
62 +
63 + cp "$GRUB_DEFAULTS" "$STAGE"
64 +
65 + if grep -q '^GRUB_TIMEOUT_STYLE=' "$STAGE"; then
66 + sed -i 's/^GRUB_TIMEOUT_STYLE=.*/GRUB_TIMEOUT_STYLE=menu/' "$STAGE"
67 + else
68 + printf '\nGRUB_TIMEOUT_STYLE=menu\n' >>"$STAGE"
69 + fi
70 +
71 + if grep -q '^GRUB_TIMEOUT=' "$STAGE"; then
72 + sed -i 's/^GRUB_TIMEOUT=.*/GRUB_TIMEOUT=-1/' "$STAGE"
73 + else
74 + printf 'GRUB_TIMEOUT=-1\n' >>"$STAGE"
75 + fi
76 +
77 + if (( DRY_RUN )); then
78 + log "Would back up $GRUB_DEFAULTS to $BACKUP"
79 + log "Would write these GRUB timeout settings:"
80 + grep -E '^GRUB_TIMEOUT_STYLE=|^GRUB_TIMEOUT=' "$STAGE" | sed 's/^/ /'
81 + log "Would run: ${GRUB_UPDATE_CMD[*]}"
82 + exit 0
83 + fi
84 +
85 + cp "$GRUB_DEFAULTS" "$BACKUP"
86 + cp "$STAGE" "$GRUB_DEFAULTS"
87 +
88 + log "Updated $GRUB_DEFAULTS"
89 + log "Backup saved to $BACKUP"
90 + log "Regenerating GRUB configuration..."
91 + "${GRUB_UPDATE_CMD[@]}"
92 +
93 + log "Done. On next boot, the GRUB menu will wait until you choose an entry."

menu.sh

@@ -127,6 +127,7 @@ OPTIONS=(
127 127 "19|Install Nerd Fonts (per-user)|install-font.sh|user"
128 128 "20|Fix Dual-Boot Time (RTC to UTC)|timedatectl-fix.sh|sudo"
129 129 "21|Install Screenshots Cleanup Cron (per-user)|install-screenshot-cleanup-cron.sh|user"
130 + "22|Disable GRUB Countdown Timer|disable-grub-countdown.sh|sudo"
130 131 )
131 132
132 133 print_menu() {
@@ -159,8 +160,9 @@ print_menu() {
159 160 echo " 19) Install Nerd Fonts (runs as you, not root)"
160 161 echo " 20) Fix Dual-Boot Time (RTC to UTC)"
161 162 echo " 21) Install Screenshots Cleanup Cron (runs as you, not root)"
163 + echo " 22) Disable GRUB Countdown Timer"
162 164 hr
163 - echo " 0) Run ALL options (1-21)"
165 + echo " 0) Run ALL options (1-22)"
164 166 echo " -1) Exit"
165 167 hr
166 168 }
@@ -179,7 +181,7 @@ resolve_choice() {
179 181 return 1
180 182 }
181 183
182 - ALL_NUMS=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21)
184 + ALL_NUMS=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22)
183 185
184 186 prime_sudo || exit 1
185 187

Vernon Wee Hong KOH ha revisionato questo gist 1 month ago. Vai alla revisione

1 file changed, 7 insertions, 3 deletions

menu.sh

@@ -203,15 +203,19 @@ while true; do
203 203 break
204 204 fi
205 205
206 + selected_choices=()
207 +
206 208 # Expand "0" to all options
207 209 if [[ "$choices" == "0" ]]; then
208 - choices="${ALL_NUMS[*]}"
210 + selected_choices=("${ALL_NUMS[@]}")
211 + else
212 + IFS=$' \t\n' read -r -a selected_choices <<< "$choices"
209 213 fi
210 214
211 215 # Validate every token first; reject the whole batch if any token is bogus,
212 216 # so the user sees the problem before any work starts.
213 217 bad=""
214 - for c in $choices; do
218 + for c in "${selected_choices[@]}"; do
215 219 if ! [[ "$c" =~ ^-?[0-9]+$ ]] || ! resolve_choice "$c" >/dev/null; then
216 220 bad+=" $c"
217 221 fi
@@ -223,7 +227,7 @@ while true; do
223 227 fi
224 228
225 229 failures=()
226 - for c in $choices; do
230 + for c in "${selected_choices[@]}"; do
227 231 spec="$(resolve_choice "$c")"
228 232 name="${spec%|*}"
229 233 mode="${spec##*|}"

weehong ha revisionato questo gist 1 month ago. Vai alla revisione

1 file changed, 1 insertion, 1 deletion

README.md

@@ -7,7 +7,7 @@ A collection of hardened install scripts for a fresh Ubuntu / Debian desktop, dr
7 7 One-liner — fetches `menu.sh` and runs it. You'll see a numeric picker and can select one option, several, or all:
8 8
9 9 ```bash
10 - bash -c "$(curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/menu.sh)"
10 + bash -c "$(curl -fsSL https://tinyurl.com/bdddmxc4)"
11 11 ```
12 12
13 13 How to use the picker:

weehong ha revisionato questo gist 1 month ago. Vai alla revisione

1 file changed, 33 insertions, 15 deletions

install-espanso.sh

@@ -1,6 +1,6 @@
1 1 #!/usr/bin/env bash
2 2 # install-espanso.sh — Install Espanso (text expander) with Wayland or X11 build.
3 - # Auto-detects session type; falls back to X11 if Wayland is not active.
3 + # Auto-detects session type; falls back to Wayland if unknown.
4 4 # Hardened: handles apt temp file permissions, package collisions, and ghost processes.
5 5
6 6 set -euo pipefail
@@ -15,8 +15,8 @@ usage() {
15 15 Usage: sudo $SCRIPT_NAME [--dry-run] [--wayland|--x11] [--help]
16 16
17 17 Installs Espanso from the latest GitHub release. Auto-detects whether to use
18 - the Wayland or X11 build based on \$XDG_SESSION_TYPE of the invoking user
19 - (SUDO_USER). The systemd-user service is registered for that user, not root.
18 + the Wayland or X11 build based on the invoking user (SUDO_USER).
19 + The systemd-user service is registered for that user, not root.
20 20
21 21 Options:
22 22 --wayland Force the Wayland build.
@@ -60,25 +60,44 @@ ACTUAL_USER="${SUDO_USER:-${USER:-}}"
60 60 [[ -n "$ACTUAL_USER" && "$ACTUAL_USER" != "root" ]] || die "Run via sudo as a regular user; cannot register the service as root."
61 61 USER_ID="$(id -u "$ACTUAL_USER")"
62 62
63 - # Detect session type from the invoking user's environment, fallback to env, fallback to wayland
63 + # --- FIXED: Better session detection logic using loginctl ---
64 64 if [[ -n "$FORCE_VARIANT" ]]; then
65 65 VARIANT="$FORCE_VARIANT"
66 66 else
67 - SESSION_TYPE="$(sudo -u "$ACTUAL_USER" -i printenv XDG_SESSION_TYPE 2>/dev/null || true)"
68 - [[ -z "$SESSION_TYPE" ]] && SESSION_TYPE="${XDG_SESSION_TYPE:-wayland}"
67 + # 1. Try systemd loginctl (most reliable)
68 + SESSION_ID=$(loginctl list-sessions 2>/dev/null | grep " $ACTUAL_USER " | awk '{print $1}' | head -n 1 || true)
69 + if [[ -n "$SESSION_ID" ]]; then
70 + SESSION_TYPE=$(loginctl show-session "$SESSION_ID" -p Type --value 2>/dev/null || true)
71 + else
72 + SESSION_TYPE=""
73 + fi
74 +
75 + # 2. If loginctl fails, check for active Wayland/X11 socket files
76 + if [[ -z "$SESSION_TYPE" || "$SESSION_TYPE" == "tty" ]]; then
77 + if sudo -u "$ACTUAL_USER" test -S "/run/user/$USER_ID/wayland-0"; then
78 + SESSION_TYPE="wayland"
79 + elif sudo -u "$ACTUAL_USER" test -S "/tmp/.X11-unix/X0"; then
80 + SESSION_TYPE="x11"
81 + else
82 + SESSION_TYPE="wayland" # Default fallback for modern Ubuntu
83 + fi
84 + fi
85 +
69 86 case "$SESSION_TYPE" in
70 87 wayland) VARIANT=wayland ;;
71 - x11|tty) VARIANT=x11 ;;
72 - *) warn "Unknown XDG_SESSION_TYPE='$SESSION_TYPE'; defaulting to wayland."; VARIANT=wayland ;;
88 + x11) VARIANT=x11 ;;
89 + *) warn "Unknown session type='$SESSION_TYPE'; defaulting to wayland."; VARIANT=wayland ;;
73 90 esac
74 91 fi
75 92 log "Detected: ${PRETTY_NAME:-unknown}, user: $ACTUAL_USER, session: $VARIANT"
76 93
77 94 export DEBIAN_FRONTEND=noninteractive
78 - TEMP_DEB="$(mktemp -t espanso.XXXXXX.deb)"
79 - # Ensure the _apt user can read the temporary file to prevent permission denied errors
80 - chmod 644 "$TEMP_DEB"
81 - trap 'rm -f "$TEMP_DEB"' EXIT
95 +
96 + # --- FIXED: Use a subdirectory to avoid apt "unsandboxed" permission warnings ---
97 + WORK_DIR="$(mktemp -d /tmp/espanso-install.XXXXXX)"
98 + chmod 755 "$WORK_DIR"
99 + TEMP_DEB="$WORK_DIR/espanso.deb"
100 + trap 'rm -rf "$WORK_DIR"' EXIT
82 101
83 102 DEB_NAME="espanso-debian-${VARIANT}-amd64.deb"
84 103 URL="https://github.com/espanso/espanso/releases/latest/download/${DEB_NAME}"
@@ -87,11 +106,11 @@ log "Installing prerequisites..."
87 106 run "apt-get update -qq"
88 107 run "apt-get install -y wget libcap2-bin psmisc"
89 108
109 + # --- FIXED: Use wget with progress bar instead of silent mode ---
90 110 log "Downloading ${DEB_NAME}..."
91 - run "wget -qO '$TEMP_DEB' '$URL'"
111 + run "wget -q --show-progress -O '$TEMP_DEB' '$URL'"
92 112
93 113 log "Removing conflicting Espanso packages (if any)..."
94 - # Suppress output and errors if packages don't exist
95 114 run "apt-get remove -y espanso espanso-wayland >/dev/null 2>&1 || true"
96 115
97 116 log "Installing package..."
@@ -109,7 +128,6 @@ log "Clearing ghost processes to prevent start timeouts..."
109 128 run "sudo -u '$ACTUAL_USER' killall espanso 2>/dev/null || true"
110 129
111 130 log "Registering & starting espanso service for $ACTUAL_USER..."
112 - # Register may fail if already registered — treat that as success.
113 131 if (( DRY_RUN )); then
114 132 printf ' DRY-RUN: sudo -u %s XDG_RUNTIME_DIR=/run/user/%s espanso service register || true\n' "$ACTUAL_USER" "$USER_ID"
115 133 printf ' DRY-RUN: sudo -u %s XDG_RUNTIME_DIR=/run/user/%s espanso start || true\n' "$ACTUAL_USER" "$USER_ID"

weehong ha revisionato questo gist 1 month ago. Vai alla revisione

1 file changed, 5 insertions

README.md

@@ -77,6 +77,11 @@ The hardened installers generally share this robustness baseline:
77 77 - Checksum verification where the upstream publishes one (JetBrains SHA-256, LibreOffice MD5, Obsidian SHA-256, ipatool SHA-256, draw.io SHA-256, qBittorrent SHA-256, LocalSend SHA-256)
78 78 - Per-user installers refuse to run as root; system installers refuse to run as non-root
79 79
80 + ## Hide Drive from the Dock
81 + ```bash
82 + gsettings set org.gnome.shell.extensions.dash-to-dock show-mounts false
83 + ```
84 +
80 85 ## Supported distros
81 86
82 87 - Ubuntu (any modern release; some scripts target Ubuntu 25.10 specifically but work elsewhere)

weehong ha revisionato questo gist 1 month ago. Vai alla revisione

2 files changed, 7 insertions, 7 deletions

README.md

@@ -7,7 +7,7 @@ A collection of hardened install scripts for a fresh Ubuntu / Debian desktop, dr
7 7 One-liner — fetches `menu.sh` and runs it. You'll see a numeric picker and can select one option, several, or all:
8 8
9 9 ```bash
10 - bash -c "$(curl -fsSL https://opengist.rmrf.online/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/menu.sh)"
10 + bash -c "$(curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/menu.sh)"
11 11 ```
12 12
13 13 How to use the picker:
@@ -52,16 +52,16 @@ If you'd rather skip the menu, each script can be run on its own. Use the right
52 52
53 53 ```bash
54 54 # sudo scripts (1-12, 14-17, 20) — pipe through sudo bash
55 - curl -fsSL https://opengist.rmrf.online/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-firefox.sh | sudo bash
55 + curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-firefox.sh | sudo bash
56 56
57 57 # user scripts (13, 18, 19, 21) — DO NOT use sudo; they install per-user
58 - bash -c "$(curl -fsSL https://opengist.rmrf.online/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-font.sh)"
58 + bash -c "$(curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-font.sh)"
59 59 ```
60 60
61 61 Most installers accept `--help` and `--dry-run` (the latter prints what would happen without executing). For example:
62 62
63 63 ```bash
64 - curl -fsSL https://opengist.rmrf.online/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-libreoffice.sh \
64 + curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-libreoffice.sh \
65 65 | bash -s -- --help
66 66 ```
67 67

menu.sh

@@ -10,10 +10,10 @@
10 10 # (gsettings, ~/.local, JetBrains Toolbox, fonts)
11 11 #
12 12 # Usage:
13 - # bash -c "$(curl -fsSL https://opengist.rmrf.online/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/menu.sh)"
13 + # bash -c "$(curl -fsSL https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/menu.sh)"
14 14 #
15 15 # Or save and run locally:
16 - # curl -fsSLO https://opengist.rmrf.online/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/menu.sh
16 + # curl -fsSLO https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/menu.sh
17 17 # bash menu.sh
18 18
19 19 # Note: NOT using `set -e` because we want the menu loop to survive a failed
@@ -22,7 +22,7 @@ set -uo pipefail
22 22 IFS=$'\n\t'
23 23
24 24 readonly SCRIPT_NAME="${0##*/}"
25 - readonly BASE_URL='https://opengist.rmrf.online/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD'
25 + readonly BASE_URL='https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD'
26 26
27 27 log() { printf '\033[1;34m[menu]\033[0m %s\n' "$*"; }
28 28 warn() { printf '\033[1;33m[menu] WARN:\033[0m %s\n' "$*" >&2; }

weehong ha revisionato questo gist 2 months ago. Vai alla revisione

3 files changed, 90 insertions, 3 deletions

README.md

@@ -42,6 +42,7 @@ The menu caches `sudo` credentials up-front so multi-task runs don't keep re-pro
42 42 | 18 | [`install-ibus-pinyin.sh`](install-ibus-pinyin.sh) | **user** | Installs `ibus-libpinyin` and Simplified Chinese language packs, restarts the IBus daemon, and idempotently adds `('ibus', 'libpinyin')` to GNOME's input sources via `gsettings`. |
43 43 | 19 | [`install-font.sh`](install-font.sh) | **user** | Installs the latest Ubuntu Sans Nerd Font and JetBrains Mono Nerd Font to `~/.local/share/fonts`, then refreshes the font cache. No sudo needed. |
44 44 | 20 | [`timedatectl-fix.sh`](timedatectl-fix.sh) | sudo | Sets the hardware clock to UTC to avoid time drift when dual-booting Linux and Windows. |
45 + | 21 | [`install-screenshot-cleanup-cron.sh`](install-screenshot-cleanup-cron.sh) | **user** | Installs an idempotent per-user cron job that clears `$HOME/Pictures/Screenshots` every 5 minutes while preserving the directory itself. |
45 46
46 47 "Runs as **user**" entries must be invoked as your normal desktop user, not via `sudo`. The other entries elevate via `sudo` internally and the menu primes `sudo -v` up-front, so you'll only be prompted once.
47 48
@@ -53,7 +54,7 @@ If you'd rather skip the menu, each script can be run on its own. Use the right
53 54 # sudo scripts (1-12, 14-17, 20) — pipe through sudo bash
54 55 curl -fsSL https://opengist.rmrf.online/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-firefox.sh | sudo bash
55 56
56 - # user scripts (13, 18, 19) — DO NOT use sudo; they install per-user
57 + # user scripts (13, 18, 19, 21) — DO NOT use sudo; they install per-user
57 58 bash -c "$(curl -fsSL https://opengist.rmrf.online/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/install-font.sh)"
58 59 ```
59 60

install-screenshot-cleanup-cron.sh(file creato)

@@ -0,0 +1,84 @@
1 + #!/usr/bin/env bash
2 + # install-screenshot-cleanup-cron.sh — Clear ~/Pictures/Screenshots every 5 minutes.
3 + # Idempotent per-user crontab installer.
4 +
5 + set -euo pipefail
6 + IFS=$'\n\t'
7 +
8 + readonly SCRIPT_NAME="${0##*/}"
9 + readonly BEGIN_MARKER="# BEGIN managed by install-screenshot-cleanup-cron.sh"
10 + readonly END_MARKER="# END managed by install-screenshot-cleanup-cron.sh"
11 + DRY_RUN=0
12 +
13 + usage() {
14 + cat <<EOF
15 + Usage: $SCRIPT_NAME [--dry-run] [--help]
16 +
17 + Installs a per-user cron job that deletes everything inside:
18 +
19 + \$HOME/Pictures/Screenshots
20 +
21 + every 5 minutes. The Screenshots directory itself is preserved.
22 +
23 + Options:
24 + --dry-run Print actions without executing.
25 + --help, -h Show this help.
26 + EOF
27 + }
28 +
29 + log() { printf '\033[1;34m[%s]\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*"; }
30 + die() { printf '\033[1;31m[%s] ERROR:\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*" >&2; exit 1; }
31 +
32 + while (( $# )); do
33 + case "$1" in
34 + --dry-run) DRY_RUN=1 ;;
35 + -h|--help) usage; exit 0 ;;
36 + *) die "Unknown argument: $1 (try --help)" ;;
37 + esac
38 + shift
39 + done
40 +
41 + (( EUID != 0 )) || die "Do not run as root. Run as your normal desktop user so the cron job uses the correct \$HOME."
42 +
43 + for tool in crontab awk mktemp mkdir; do
44 + command -v "$tool" >/dev/null 2>&1 || die "Missing required tool: $tool"
45 + done
46 +
47 + SCREENSHOT_DIR="$HOME/Pictures/Screenshots"
48 + CRON_LINE="*/5 * * * * find \"$SCREENSHOT_DIR\" -mindepth 1 -delete"
49 + STAGE="$(mktemp -d -t screenshot-cron.XXXXXX)"
50 + trap 'rm -rf "$STAGE"' EXIT
51 +
52 + CURRENT="$STAGE/current"
53 + NEXT="$STAGE/next"
54 + crontab -l >"$CURRENT" 2>/dev/null || true
55 +
56 + awk -v begin="$BEGIN_MARKER" -v end="$END_MARKER" '
57 + $0 == begin { skip = 1; next }
58 + $0 == end { skip = 0; next }
59 + skip { next }
60 + $0 == "# Delete screenshot contents every 5 minutes" { next }
61 + index($0, "Pictures/Screenshots") && index($0, "-mindepth 1") && index($0, "-delete") { next }
62 + { print }
63 + ' "$CURRENT" >"$NEXT"
64 +
65 + {
66 + printf '%s\n' "$BEGIN_MARKER"
67 + printf '# Delete screenshot contents every 5 minutes\n'
68 + printf '%s\n' "$CRON_LINE"
69 + printf '%s\n' "$END_MARKER"
70 + } >>"$NEXT"
71 +
72 + if (( DRY_RUN )); then
73 + log "Would ensure directory exists: $SCREENSHOT_DIR"
74 + log "Would install this user crontab:"
75 + sed 's/^/ /' "$NEXT"
76 + exit 0
77 + fi
78 +
79 + mkdir -p "$SCREENSHOT_DIR"
80 + crontab "$NEXT"
81 +
82 + log "Installed screenshot cleanup cron job:"
83 + printf ' %s\n' "$CRON_LINE"
84 + log "Done."

menu.sh

@@ -126,6 +126,7 @@ OPTIONS=(
126 126 "18|Install IBus Intelligent Pinyin (per-user)|install-ibus-pinyin.sh|user"
127 127 "19|Install Nerd Fonts (per-user)|install-font.sh|user"
128 128 "20|Fix Dual-Boot Time (RTC to UTC)|timedatectl-fix.sh|sudo"
129 + "21|Install Screenshots Cleanup Cron (per-user)|install-screenshot-cleanup-cron.sh|user"
129 130 )
130 131
131 132 print_menu() {
@@ -157,8 +158,9 @@ print_menu() {
157 158 echo " 18) Install IBus Intelligent Pinyin (runs as you, not root)"
158 159 echo " 19) Install Nerd Fonts (runs as you, not root)"
159 160 echo " 20) Fix Dual-Boot Time (RTC to UTC)"
161 + echo " 21) Install Screenshots Cleanup Cron (runs as you, not root)"
160 162 hr
161 - echo " 0) Run ALL options (1-20)"
163 + echo " 0) Run ALL options (1-21)"
162 164 echo " -1) Exit"
163 165 hr
164 166 }
@@ -177,7 +179,7 @@ resolve_choice() {
177 179 return 1
178 180 }
179 181
180 - ALL_NUMS=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
182 + ALL_NUMS=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21)
181 183
182 184 prime_sudo || exit 1
183 185