项目作者: prasannavl

项目描述 :
My portable dotfiles
高级语言: Lua
项目地址: git://github.com/prasannavl/dotfiles.git
创建时间: 2018-10-14T17:53:16Z
项目社区:https://github.com/prasannavl/dotfiles

开源协议:

下载


dotpkg

My portable dot-packages.

Zero-conf, zero-dep with a simple bash script that focuses on cognitive simplicity
and natural workflows while allowing maximum flexibility for each pkg to be modular
and set its own rules of how to link, install and cleanup.

I’ve used various dot patterns out there in the last 2 decade from stow (longest)
to ansible (next) to fancy managers like chez_moi, dotter or nix all the way with
home-manager (love nix for flakes and pkg management). However, I’ve never enjoyed
the complexity as it grows just to keep my config and DRY on my essentials.
This is an attempt to simplify and make life sane.

Note: I tend to work as close to defaults as possible and minimal configuration
in most cases, except for some essential foundations or esoteric things that I use
often (e.g., my tmux prefix is Alt+E: so it’s one-hand accessible, doesn’t conflict
and doesn’t stress my finger muscles for doing this all day).

Read the pkg.mod.sh file in each pkg for a quick idea on conventions.

Guide

Zero conf

  • Create a dir to repr new package (eg: bash)
  • Add your files
  • Run ./pkg.sh sync bash to link them to your home dir
  • That’s it. See bash for a real example.
  1. bash/
  2. ├── .bashrc
  3. ├── .profile
  4. └── .bashrc.d/
  5. ├── aliases.sh
  6. └── functions.sh

Light conf

  • Create a dir to repr new package (eg: nvim)
  • Add your files
  • Create pkg.mod.sh inside the dir for config
  • Override the vars function and set LINKS array var to take control of want to link.
  1. vars() {
  2. LINKS=(.config/nvim)
  3. }
  • Run ./pkg.sh sync nvim to link them to your home dir.
  • See nvim for a real example.
  1. nvim/
  2. ├── .config/nvim/<files>
  3. └── pkg.mod.sh

Light-ish conf

  • Create a dir to repr new package (eg: tmux)
  • Add your files
  • Create pkg.mod.sh inside the dir for config
  • Override the vars function and set LINKS array var to set tmux conf only.
  • Override post_link to setup tpm plugin manager and install plugins.
  • Override pre_unlink to cleanup tpm dir.
  • Be a good citizen and add full conf cleanup to clean_conf
  1. vars() {
  2. TMUX_PLUGIN_DIR="$HOME/.tmux/plugins"
  3. TMUX_TPM_DIR="$TMUX_PLUGIN_DIR/tpm"
  4. LINKS=(
  5. .tmux.conf
  6. )
  7. }
  8. post_link() {
  9. rm -rf "$TMUX_TPM_DIR"
  10. git clone https://github.com/tmux-plugins/tpm "$TMUX_TPM_DIR"
  11. $TMUX_TPM_DIR/bin/install_plugins
  12. }
  13. pre_unlink() {
  14. rm -rf "$TMUX_TPM_DIR"
  15. }
  16. clean_conf() {
  17. rm -rf "$TMUX_PLUGIN_DIR"
  18. }
  • Run ./pkg.sh sync tmux to see it in action - your tmux conf linked
    and plugins installed - you’re all set.
  • See tmux for a real example.
  1. tmux/
  2. ├── .tmux.conf
  3. └── pkg.mod.sh

Medium conf

  • The above tmux is nice. But what if you want to install tmux itself alongside the config as one unit?
  • You can! Use the same steps above. Just override install, uninstall commands.
  1. vars() {
  2. TMUX_PLUGIN_DIR="$HOME/.tmux/plugins"
  3. TMUX_TPM_DIR="$TMUX_PLUGIN_DIR/tpm"
  4. LINKS=(
  5. .tmux.conf
  6. )
  7. }
  8. post_link() {
  9. rm -rf "$TMUX_TPM_DIR"
  10. git clone https://github.com/tmux-plugins/tpm "$TMUX_TPM_DIR"
  11. $TMUX_TPM_DIR/bin/install_plugins
  12. }
  13. pre_unlink() {
  14. rm -rf "$TMUX_TPM_DIR"
  15. }
  16. clean_conf() {
  17. rm -rf "$TMUX_PLUGIN_DIR"
  18. }
  19. check_install() {
  20. command -v tmux > /dev/null
  21. }
  22. install() {
  23. sudo apt install tmux
  24. }
  25. uninstall() {
  26. sudo apt purge tmux --autoremove
  27. }
  • Run ./pkg.sh sync tmux to link and setup tpm. tmux will be installed if needed,
    your tmux conf linked and plugins installed. Now you’re really set.
  • This is handy so you don’t need to worry about disconnect between your conf
    and what’s actually installed - if conf is there, so is your runtime - in one go.
  • See tmux for a real example.

Alt conf

  • This is nice. The first dot repo you get into any machine, but can I use this to install
    and setup tools base tools like deno or rust that need DRY but isn’t the distro repo?
  • Yep! Just override install, uninstall and leave LINKS to default of ().

Deno

  1. ```bash
  2. vars() {
  3. BASH_COMPLETIONS_DIR=$HOME/.bashrc.d/completions
  4. }
  5. check_install() {
  6. command -v deno > /dev/null
  7. }
  8. install() {
  9. curl -fsSL https://deno.land/x/install/install.sh | sh
  10. after_install
  11. }
  12. after_install() {
  13. deno completions bash > "$BASH_COMPLETIONS_DIR/deno"
  14. }
  15. uninstall() {
  16. rm -f "$BASH_COMPLETIONS_DIR/deno"
  17. rm -rf "$HOME/.deno"
  18. }
  • Run ./pkg.sh sync deno and you’re all set.
  • See deno for a real example.

Rust

Here’s another for Rust toolchain with rustup:

  1. vars() {
  2. BASH_COMPLETIONS_DIR=$HOME/.bashrc.d/completions
  3. COMPLETION_BINS=(
  4. rustup
  5. cargo
  6. )
  7. }
  8. check_install() {
  9. command -v rustup > /dev/null
  10. }
  11. install() {
  12. # for all options:
  13. # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --help
  14. curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --no-modify-path -y
  15. after_install
  16. }
  17. after_install() {
  18. for bin in "${COMPLETION_BINS[@]}"; do
  19. rustup completions bash $bin > "$BASH_COMPLETIONS_DIR/$bin"
  20. done
  21. rustup component add rust-analyzer
  22. rustup target add wasm32-unknown-unknown
  23. rustup toolchain add nightly
  24. }
  25. uninstall() {
  26. # remove completions
  27. for bin in "${COMPLETION_BINS[@]}"; do
  28. rm -f "$BASH_COMPLETIONS_DIR/$bin"
  29. done
  30. rustup self uninstall -y
  31. rm -rf "$HOME/.rustup"
  32. }
  33. purge() {
  34. rm -rf "$HOME/.cargo"
  35. }
  • Run ./pkg.sh sync rust and you’re all set.
  • See rust for a real example.

Under the hood

The entire working is probably best explained by reading the tiny ./pkg.sh file.
If you don’t have time, these lines are the key logic - that’s it.

  1. local x
  2. for x in "${pkgs[@]}"; do
  3. echo "=== $x ==="
  4. pushd "./$x" >/dev/null
  5. (
  6. # Note that we run all of these in a subshell giving it's mod file the
  7. # ability to override anything from this file and start fresh again.
  8. source "./$pkg_mod_file" 2>/dev/null || true
  9. run_cmd "$cmd"
  10. )
  11. popd >/dev/null
  12. done
  • Everything else is just sane default impls, safe link guards and intuitive lifecycle.
  • Most of the script is comments and help text. The actual logic is minimal convention
    plumbing - you can use this for anything without having to learn a new tool
    or language or configuration.
  • The key trick is using subshells for each pkg to make bash behave like a
    it has nice default impls in pkg.sh while allowing you to override everything and
    keeping surface area small and intuitive.

Process

  • Create a new pkg dir in the root (e.g., tmux), with whichever files (.tmux.conf).
  • For optional additional config, create pkg.mod.sh file inside the pkg dir
    • See lifecycle section below on flow.
    • The name of this file is configurable (PKG_MOD_SH)
  • Run with ./pkg.sh <command> <package1> [<package2> ...] to execute commands for
    specific packages.
  • You can specify multiple packages to operate on in a single command.

Lifecycle

  • The main pkg.sh file does the following:
    • For each specified package:
      • Sources pkg.mod.sh file if available
      • Runs vars so you can override them all.
      • Runs the specified command (e.g., sync, link, install, clean etc.)
    • Default impl for all of the commands exist in pkg.sh
      • Default impl for vars:
        • LINKS=() (nothing will get linked)
      • Default impl of link|unlink
        • Call pre_link | pre_unlink if exists
        • For each file in LINKS var, link them inside TARGET dir
        • Call post_link | post_unlink if exists
        • This allows your package to flexibly choose whichever model:
        • Just add LINKS in vars to autolink and use pre_ and post_
          hooks for additional work
          • Or override link and unlink completely and choose your own mechanism
            entirely.
    • This allows you to have a simple and consistent way to manage your dotfiles
      while allowing each package to override all of the impl if default convention
      isn’t enough.

Misc

  • install|uninstall:
    • This extends the capability to also use a package manager to
      install what’s needed before deploying the config, so everything is contained in
      one set. Allows you to simply deploy whole packages or nothing on different hosts.
    • Please note that this is not meant to be yet another manager for your distro.
      It’s just handy to have this so you can use any source you want. What if your
      preferred way to get nvim is from source or linuxbrew or if you’re like me and
      prefer to install latest version from nix than your operating system.
    • Can this be made platform agnostic? Right now it all uses apt since
      Debian is what I use and haven’t bothered to put in the effort to add APT_PKGS
      and DNF_PKGS or BREW_PKGS. But should be trivial to extend default install
      impl to support this the same way link hooks use a default impl and offer
      after and before hooks for flexibility.
  • Host specific config: It’s just a simple script that takes pkgs as input. Create
    new files with as many sets as needed. See (xset dir for example)
  • Templating: I prefer my dot file management to be simple, so it is agnostic
    of how to template. Use the above pre/post install hooks to use any templating
    engine for each pkg as desired. Low cognitive overhead and flexible to allow
    different templating based on a pkg’s need than dump one on you.

Usage

  1. usage: <command> <package1> [<package2> ...]
  2. env:
  3. TARGET: (target to install to, defaults to HOME env)
  4. PKG_MOD_SH: (the mod sh file to use for each module)
  5. FORCE_RELINK: (force relink without requiring unlink or fail for safety)
  6. commands:
  7. sync: brings everything up to date: check_install [install], [re]link
  8. install: install the package
  9. check_install: check if package requires installation
  10. uninstall: uninstall the package
  11. link: link the package, can fail if links already exist
  12. relink: force re-link the package
  13. unlink: unlink the package
  14. clean_conf: clean the package configuration
  15. clean: unlink, clean_conf and cleans other files
  16. purge: uninstall, clean and purge other files

Example: ./pkg.sh sync bash tmux rust deno will make sure all these pkgs
are installed, configured and ready to go!