|
| 1 | +--- |
| 2 | +title: Use the latest Dev Tools on a Stable Debian Linux |
| 3 | +date: |
| 4 | + created: 2026-03-29 |
| 5 | + updated: 2026-03-30 |
| 6 | +authors: |
| 7 | + - practicalli |
| 8 | +categories: |
| 9 | + - debian |
| 10 | +tags: |
| 11 | + - operating-system |
| 12 | + - bash |
| 13 | + - scripting |
| 14 | +draft: false |
| 15 | +--- |
| 16 | + |
| 17 | +{align=right loading=lazy style="width:240px"} |
| 18 | + |
| 19 | +I use Debian Linux as there is a huge advantage to having a stable and low maintenance operating system. This allows me to focus and get more things done. |
| 20 | + |
| 21 | +The constraint of a stable operating system is that some of the latest versions of development tools and programming languages may not be available as part of the distributions package manager. |
| 22 | + |
| 23 | +Simple bash scripts were created to install the latest development tools, made effectively one-liner's where the [Download Release Artifacts (DRA)](#Git} project could be used. The scripts were very simple even when falling back to `curl` with a few basic Linux commands. |
| 24 | + |
| 25 | +These shell scripts and the DRA tool are used to install editors, programming languages (e.g. Clojure, Rust, Node.js), Terminal UI (TUI's) tools for development or system administration and even the odd desktop app. |
| 26 | + |
| 27 | +A `debian-linux-post-instal.sh` script was created update all the Debian packages, reading packages to add and remove from a plain text file. |
| 28 | + |
| 29 | +A `dev-tools-install.sh` calls each script that installs the latest versions of each development tool, programming language, TUI and desktop app. |
| 30 | + |
| 31 | +[Practicalli dotfiles - Debian-Linux](https://github.com/practicalli/dotfiles/tree/main/debian-linux) contains all the scripts, along with a few manual steps that have not been scripted (mainly UI / UX configuration). |
| 32 | + |
| 33 | +<!-- more --> |
| 34 | + |
| 35 | +## Debian Packages |
| 36 | + |
| 37 | +I created a simple bash script to add packages for tools and applications I commonly use. |
| 38 | + |
| 39 | +The script reads a list of packages from two text files, one listing packages to add and another listing packages to remove. |
| 40 | + |
| 41 | +Each list is just a text file, making it very easy to maintain. I keep both lists in alphabetic order so its easy to check which packages will be added or removed. |
| 42 | + |
| 43 | +The post install script also includes the setup of the clipboard package, based on which windowing system is currently used, i.e. X11 or Wayland. |
| 44 | + |
| 45 | +The script also sets `kitty` as the default terminal. Kitty would already be installed as it is part of the add package list. |
| 46 | + |
| 47 | +Finally the script tidies up packages not required, `autopurge`, and deletes all the downloaded packages, `autoclean`. |
| 48 | + |
| 49 | +> NOTE: A Debian Linux install adds a few packages that I do not wish to use, or replaces packages when I have alternative package preferences. E.g. I remove Evolution as I do not need a local mail server, I also use LightDM rather than GDM3 for the GUI greeter and display manager. |
| 50 | +
|
| 51 | + |
| 52 | +### Package Post Install Script |
| 53 | + |
| 54 | +A `while` loop is used to read through a package list file one line at a time, each time calling the `apt-get` command. |
| 55 | + |
| 56 | +`sudo apt-get --yes --ignore-missing install <package-name>` is used to install each package in turn. |
| 57 | + |
| 58 | +The `--ignore-missing` flag skips a package if it is already installed and at the latest version. |
| 59 | + |
| 60 | +The `--yes` flag automatically confirms the install, even when otherwise prompted to confirm the install of the recommended package dependencies. |
| 61 | + |
| 62 | +> NOTE: `apt-get` is the back-end command used by `apt`, the Debian Linux package manager. `apt-get` is ideal for use in scripts and has a little less overhead than `apt` itself. |
| 63 | +
|
| 64 | +??? EXAMPLE "Debian Linux Post Install Script" |
| 65 | + ```shell |
| 66 | + #!/usr/bin/env bash |
| 67 | + |
| 68 | + # Batch install Debian Linux packages used by Practicalli |
| 69 | + # Skip packages if they are already installed |
| 70 | + |
| 71 | + # NOTES: |
| 72 | + # - apt-get backend used as features of apt UI not required for scripts |
| 73 | + |
| 74 | + # Package lists to process |
| 75 | + add="debian-linux-post-install-packages-add.list" |
| 76 | + purge="debian-linux-post-install-packages-purge.list" |
| 77 | + |
| 78 | + ## Update available Debian packages |
| 79 | + echo |
| 80 | + echo "# ---------------------------------------" |
| 81 | + echo Update available Debian packages |
| 82 | + sudo apt update |
| 83 | + echo "# ---------------------------------------" |
| 84 | + echo |
| 85 | + |
| 86 | + # Install additional Debian packages |
| 87 | + while read -r line |
| 88 | + do |
| 89 | + echo "# ---------------------------------------" |
| 90 | + echo Install "$line" |
| 91 | + sudo apt-get --yes --ignore-missing install "$line" |
| 92 | + echo "# ---------------------------------------" |
| 93 | + done < "$add" |
| 94 | + echo |
| 95 | + |
| 96 | + echo "# ---------------------------------------" |
| 97 | + echo Install System Clipboard tool - X11 or Wayland |
| 98 | + ./clipboard.sh |
| 99 | + echo "# ---------------------------------------" |
| 100 | + echo |
| 101 | + |
| 102 | + echo "# ---------------------------------------" |
| 103 | + echo Configure Kitty as default terminal |
| 104 | + sudo update-alternatives --set x-terminal-emulator "/usr/bin/kitty" |
| 105 | + echo "# ---------------------------------------" |
| 106 | + echo |
| 107 | + |
| 108 | + # Remove additional Debian packages |
| 109 | + while read -r line |
| 110 | + do |
| 111 | + echo |
| 112 | + echo "# ---------------------------------------" |
| 113 | + echo Purge "$line" |
| 114 | + echo "# ---------------------------------------" |
| 115 | + echo |
| 116 | + sudo apt-get purge --yes --ignore-missing "$line" |
| 117 | + done < "$purge" |
| 118 | + echo |
| 119 | + |
| 120 | + echo "# ---------------------------------------" |
| 121 | + echo Uninstall unnecessary Debian packages |
| 122 | + sudo apt-get autopurge |
| 123 | + echo "# ---------------------------------------" |
| 124 | + echo |
| 125 | + |
| 126 | + echo "# ---------------------------------------" |
| 127 | + echo Remove Debian package files from cache |
| 128 | + sudo apt-get clean |
| 129 | + echo "# ---------------------------------------" |
| 130 | + echo |
| 131 | + ``` |
| 132 | + |
| 133 | +## Development tools |
| 134 | + |
| 135 | +I use Neovim and Emacs editors, a range of development tools and several programming languages. |
| 136 | + |
| 137 | +I created a simple bash script for each each development tool. Each script downloads and installs the binary or AppImage file for that tool. |
| 138 | + |
| 139 | +Where relevant, the script also updates shell completions (for either bash or zsh, depending on which is currently being used). |
| 140 | + |
| 141 | +The following tools are used to simplify the scripts: |
| 142 | + |
| 143 | +- [Download Release Assets (DRA)](https://github.com/devmatteini/dra) downloads the latest stable release from a GitHub repository |
| 144 | +- [Uv](https://docs.astral.sh/uv/) to install python packages, using `uv tool install` to avoid the need for a Python virtual environment. |
| 145 | +- Curl with a tools specific install script, e.g. Clojure CLI. |
| 146 | +- Curl with a little scripting magic as a fall-back |
| 147 | + |
| 148 | +[Practicalli Dotfiles](https://github.com/practicalli/dotfiles/) contains a [debian-linux](https://github.com/practicalli/dotfiles/tree/main/debian-linux) directory with the individual tool scripts organised in sub-directories: |
| 149 | + |
| 150 | +- `app` for desktop applications |
| 151 | +- `cli` for command line tools |
| 152 | +- `language` for programming languages, e.g. Clojure, Rust, Node.js |
| 153 | +- `tui` terminal UI apps used for system administration and supporting development tools |
| 154 | + |
| 155 | + |
| 156 | +### GitHub Releases |
| 157 | + |
| 158 | +Most development tools and TUI's have binaries or AppImages available from their GitHub Release pages. |
| 159 | + |
| 160 | +[Download Release Assets (DRA) tool](https://github.com/devmatteini/dra) downloads and installs the latest release from the specified GitHub repository. |
| 161 | + |
| 162 | +Unfortunately I cant use DRA to install DRA, so curl is used to download the DRA install script |
| 163 | + |
| 164 | +Alternatively, use curl and a bit of scripting magic to get the URL of the latest Debian package, using curl again to download the Debian package and the Apt package manager to install the downloaded .deb file. |
| 165 | + |
| 166 | +> NOTE: dra runs on Linux (x86_64, armv6, arm64), macOS (x86_64, arm64) and Windows. |
| 167 | +
|
| 168 | +??? EXAMPLE "Use Curl to install DRA via its install script" |
| 169 | + ```shell |
| 170 | + #!/usr/bin/env bash |
| 171 | + |
| 172 | + echo |
| 173 | + echo "# ---------------------------------------" |
| 174 | + echo "DRA - Download Release Assests from GitHub" |
| 175 | + curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/devmatteini/dra/refs/heads/main/install.sh | sudo bash -s -- --to /usr/local/bin |
| 176 | + |
| 177 | + |
| 178 | + # Generate shell command completion |
| 179 | + if [[ $SHELL == "/bin/bash" ]]; then |
| 180 | + mkdir -p ~/.local/share/bash-completion/completions/ |
| 181 | + dra completion bash > ~/.local/share/bash-completion/completions/dra |
| 182 | + |
| 183 | + elif [[ $SHELL == "/usr/bin/zsh" ]]; then |
| 184 | + mkdir -p ~/.local/share/zsh-completion |
| 185 | + dra completion zsh > ~/.local/share/zsh-completion/_dra |
| 186 | + else |
| 187 | + echo "Unknown SHELL, Ripgrep completions will not be generated" |
| 188 | + exit |
| 189 | + fi |
| 190 | + |
| 191 | + echo |
| 192 | + echo "Dra version: $(dra --version)" |
| 193 | + echo "# ---------------------------------------" |
| 194 | + echo |
| 195 | + ``` |
| 196 | + |
| 197 | + |
| 198 | +??? EXAMPLE "Use Curl to install DRA via the Debain package" |
| 199 | + DRA publishes a `.deb` file as part of each release. |
| 200 | + |
| 201 | + Curl is first used to find the URL of the .deb package from the latest GitHub release. |
| 202 | + |
| 203 | + Use `apt` the Debian package manager to install the DRA package. |
| 204 | + |
| 205 | + ```shell |
| 206 | + #!/usr/bin/env bash |
| 207 | + |
| 208 | + echo |
| 209 | + echo "# ---------------------------------------" |
| 210 | + echo "DRA - Download Release Assests from GitHub" |
| 211 | + |
| 212 | + DOWNLOAD_URL=$(curl -s https://api.github.com/repos/devmatteini/dra/releases/latest | grep browser_download_url | grep .deb | cut -d '"' -f 4) |
| 213 | + |
| 214 | + curl -s -L -o ~/tmp/dra.deb "$DOWNLOAD_URL" |
| 215 | + |
| 216 | + sudo apt install /tmp/dra.deb |
| 217 | + |
| 218 | + # Generate shell command completion |
| 219 | + if [[ $SHELL == "/bin/bash" ]]; then |
| 220 | + mkdir -p ~/.local/share/bash-completion/completions/ |
| 221 | + dra completion bash > ~/.local/share/bash-completion/completions/dra |
| 222 | + |
| 223 | + elif [[ $SHELL == "/usr/bin/zsh" ]]; then |
| 224 | + mkdir -p ~/.local/share/zsh-completion |
| 225 | + dra completion zsh > ~/.local/share/zsh-completion/_dra |
| 226 | + else |
| 227 | + echo "Unknown SHELL, Ripgrep completions will not be generated" |
| 228 | + exit |
| 229 | + fi |
| 230 | + |
| 231 | + echo |
| 232 | + echo "Dra version: $(dra --version)" |
| 233 | + echo "# ---------------------------------------" |
| 234 | + echo |
| 235 | + ``` |
| 236 | + |
| 237 | +??? INFO "Deconstructing the Debian package Shell Script" |
| 238 | + |
| 239 | + | Command | Description | |
| 240 | + | --- | --- | |
| 241 | + | `curl -s https://api.github.com/repos/USERNAME/REPO/releases/latest` | Fetch latest release data (JSON) from the GitHub repository | |
| 242 | + | `grep browser_download_url` | Filters JSON output returning only lines including a download URL | |
| 243 | + | `grep .deb` | Filters URLs to only those that are .deb files | |
| 244 | + | `cut -d '"' -f 4` | Extract URL from the filtered JSON response | |
| 245 | + | `curl -s -L -o ~/tmp/filename.deb "$DOWNLOAD_URL"` | Download `.deb` file to specified directory | |
| 246 | + |
| 247 | + |
| 248 | +DRA can also be used as a TUI to install a specific version of a tool or automatically install the latest version. |
| 249 | + |
| 250 | +{loading=lazy} |
| 251 | + |
| 252 | +### Neovim |
| 253 | + |
| 254 | +DRA is used to download the latest AppImage and install it globally, so I can use `nvim` for the `practicalli` user account and the `root` account for operating system administration. |
| 255 | + |
| 256 | +The script explicitly specifies the AppImage asset, otherwise DRA will extract and install only the `nvim` client and neglects to include the Neovim runtime. |
| 257 | + |
| 258 | +??? EXAMPLE "Install latest stable Neovim AppImage" |
| 259 | + ```shell |
| 260 | + #!/usr/bin/env bash |
| 261 | + |
| 262 | + # Install the current release version of Neovim from GitHub for all users |
| 263 | + |
| 264 | + echo |
| 265 | + echo "# ---------------------------------------" |
| 266 | + echo "Neovim hyper-configurable editor - installed for all users" |
| 267 | + |
| 268 | + # install the nvim.appimage (automatic only installs nvim and not runtime) |
| 269 | + # rename file to `nvim` the standard executable name |
| 270 | + sudo dra download --select nvim-linux-x86_64.appimage --install --output /usr/local/bin/nvim neovim/neovim |
| 271 | + |
| 272 | + echo |
| 273 | + echo "Neovim version: $(neovim --version)" |
| 274 | + echo "# ---------------------------------------" |
| 275 | + echo "" |
| 276 | + ``` |
| 277 | + |
| 278 | +A similar script is used to install a nightly release of the AppImage. |
| 279 | + |
| 280 | +This script specifies `--tag nightly` to get the latest nightly release. |
| 281 | + |
| 282 | +The script installs the AppImage into `~/.local/bin` as it is only needed for the `practicalli` user account, for testing my Neovim configuration. |
| 283 | + |
| 284 | +??? EXAMPLE "Install latest nightly Neovim AppImage" |
| 285 | + ```shell |
| 286 | + #!/usr/bin/env bash |
| 287 | + |
| 288 | + # Install the pre-release version of Neovim from GitHub, for the current user |
| 289 | + |
| 290 | + echo |
| 291 | + echo "# ---------------------------------------" |
| 292 | + echo "Neovim hyper-configurable editor" |
| 293 | + echo "- installed only for current user" |
| 294 | + |
| 295 | + # Install nvim.appimage nightly release tag and rename to nvim-nightly |
| 296 | + dra download --tag nightly --select nvim-linux-x86_64.appimage --install --output ~/.local/bin/nvim-nightly neovim/neovim |
| 297 | + |
| 298 | + echo |
| 299 | + echo "Neovim pre-release version: $(nvim-nightly --version)" |
| 300 | + echo "# ---------------------------------------" |
| 301 | + echo |
| 302 | + ``` |
| 303 | + |
| 304 | +### Emacs |
| 305 | + |
| 306 | +Emacs usually has the latest version available as a Debian package. |
| 307 | + |
| 308 | +if need a newer version or I want to try bleeding features then I will compile Emacs from its source code, choosing the relevant branch for the version I require. |
| 309 | + |
| 310 | +[Building Emacs from source code](https://practical.li/blog/build-emacs-from-source-on-debian-linux/) takes a few simple steps and can be configured to create a customised set of features and even a custom desktop icon. |
| 311 | + |
| 312 | +!!! TIP "Emacs Plus is recommended for MacOS users" |
| 313 | + [Emacs Plus](https://github.com/d12frosted/homebrew-emacs-plus) is configurable formulae for Homebrew, providing more features and flexibility than Homebrew's own Emacs formulae. |
| 314 | + |
| 315 | +## But Arch Linux is Awesome |
| 316 | + |
| 317 | +I have used 'rolling' distributions and spent 2025 testing out Arch Linux as an alternative operating system. The initial learning curve was not too steep and getting going only took a weekend. However, the maintenance burden was much greater and became a continual distraction. |
| 318 | + |
| 319 | +Part of the maintenance headache was using Btrfs (failed during a recovery) and Hyprland (which introduced lots of breaking changes). |
| 320 | + |
| 321 | +I found some core Arch Linux tools confusing. `packman` command options did not feel intuitive and the docs didn't clarify what the flags letters stood for. If I continued to use Arch Linux I would have wrapped pacman with a shell script or Makefile tasks. |
| 322 | + |
| 323 | +Using the AUR community repository I also found confusing, there are several tools to access AUR and it wasn't clear from the official docs how to add these tools without first adding AUR (catch 22 scenario). |
| 324 | + |
| 325 | +The AUR community is a big part of adding the latest development tools. Without the AURA then Arch Linux didn't feel significantly up to date for software development.. |
| 326 | + |
| 327 | +Usually the Arch Linux docs are very comprehensive, but can drowned the reader in options (some outdated) and yet not provide enough guidance or a clear and simple approach. |
| 328 | + |
| 329 | +I never did figure out a nice way to manage my SSH Keys in Arch Linux when I wasnt using a Gnome desktop. |
| 330 | + |
| 331 | +Arch Linux meets the needs of many people, but Debian Linux the ideal operating system for me. |
| 332 | + |
| 333 | +> NOTE: As I have used Debian Linux since 1995 (30 years at time of writing) I have a strong affinity and confidence using it as my operating system. Debian has never let me down. |
| 334 | +
|
| 335 | +--- |
| 336 | +Thank you. |
| 337 | + |
| 338 | +[:globe_with_meridians: Practical.li Website](https://practical.li){target=_blank .md-button} |
| 339 | + |
| 340 | +[:fontawesome-brands-github: Practical.li GitHub Org](https://github.com/practicalli){target=_blank .md-button} |
| 341 | +[:fontawesome-brands-github: practicalli-johnny profile](https://github.com/practicalli-johnny){target=_blank .md-button} |
| 342 | + |
| 343 | +[:fontawesome-brands-mastodon: @practicalli@clj.social](https://clj.social/@practicalli){target=_blank .md-button} |
| 344 | +[:fontawesome-brands-twitter: @practical_li](https://twitter.com/practcial_li){target=_blank .md-button} |
0 commit comments