{"id":16686692,"url":"https://github.com/prasannavl/dotfiles","last_synced_at":"2026-02-01T21:03:14.413Z","repository":{"id":139663500,"uuid":"153002361","full_name":"prasannavl/dotfiles","owner":"prasannavl","description":"My portable dotfiles","archived":false,"fork":false,"pushed_at":"2025-12-28T09:42:57.000Z","size":3041,"stargazers_count":0,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-30T19:10:36.508Z","etag":null,"topics":["bash-profile","configs","dotfiles","portable-dotfiles"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prasannavl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-10-14T17:53:16.000Z","updated_at":"2025-12-28T09:43:01.000Z","dependencies_parsed_at":"2024-04-26T04:30:32.810Z","dependency_job_id":"8106ea3f-8c85-4a08-9a8d-a1d3bc576b36","html_url":"https://github.com/prasannavl/dotfiles","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/prasannavl/dotfiles","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prasannavl%2Fdotfiles","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prasannavl%2Fdotfiles/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prasannavl%2Fdotfiles/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prasannavl%2Fdotfiles/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prasannavl","download_url":"https://codeload.github.com/prasannavl/dotfiles/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prasannavl%2Fdotfiles/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28990701,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T20:57:35.821Z","status":"ssl_error","status_checked_at":"2026-02-01T20:57:29.580Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash-profile","configs","dotfiles","portable-dotfiles"],"created_at":"2024-10-12T15:06:37.238Z","updated_at":"2026-02-01T21:03:14.407Z","avatar_url":"https://github.com/prasannavl.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dotpkg\n\nMy portable `dot-packages`.\n\nZero-conf, zero-dep with a simple bash script that focuses on cognitive simplicity\nand natural workflows while allowing maximum flexibility for each pkg to be modular\nand set its own rules of how to link, install and cleanup.\n\nI've used various dot patterns out there in the last 2 decade from stow (longest)\nto ansible (next) to fancy managers like chez_moi, dotter or nix all the way with\nhome-manager (love nix for flakes and pkg management). However, I've never enjoyed\nthe complexity as it grows just to keep my config and DRY on my essentials.\nThis is an attempt to simplify and make life sane.\n\n**Note:** I tend to work as close to defaults as possible and minimal configuration\nin most cases, except for some essential foundations or esoteric things that I use\noften (e.g., my tmux prefix is `Alt+E`: so it's one-hand accessible, doesn't conflict\nand doesn't stress my finger muscles for doing this all day).\n\nRead the `pkg.mod.sh` file in each pkg for a quick idea on conventions.\n\n## Guide\n\n### Zero conf\n\n- Create a dir to repr new package (eg: bash)\n- Add your files\n- Run `./pkg.sh sync bash` to link them to your home dir\n- That's it. See [bash](./bash) for a real example.\n\n```\nbash/\n├── .bashrc\n├── .profile\n└── .bashrc.d/\n    ├── aliases.sh\n    └── functions.sh\n```\n\n### Light conf\n\n- Create a dir to repr new package (eg: nvim)\n- Add your files\n- Create `pkg.mod.sh` inside the dir for config\n- Override the `vars` function and set `LINKS` array var to take control of what to link.\n\n```bash\nvars() {\n    LINKS=(.config/nvim)\n}\n```\n\n- Run `./pkg.sh sync nvim` to link them to your home dir.\n- See [nvim](./nvim/pkg.mod.sh) for a real example.\n\n```\nnvim/\n├── .config/nvim/\u003cfiles\u003e\n└── pkg.mod.sh\n```\n\n### Light-ish conf\n\n- Create a dir to repr new package (eg: tmux)\n- Add your files\n- Create `pkg.mod.sh` inside the dir for config\n- Override the `vars` function and set `LINKS` array var to set tmux conf only.\n- Override `post_link` to setup tpm plugin manager and install plugins.\n- Override `pre_unlink` to cleanup tpm dir.\n- Be a good citizen and add full conf cleanup to `clean_conf`\n\n```bash\nvars() {\n    TMUX_PLUGIN_DIR=\"$HOME/.tmux/plugins\"\n    TMUX_TPM_DIR=\"$TMUX_PLUGIN_DIR/tpm\"\n    LINKS=(\n        .tmux.conf\n        )\n}\n\npost_link() {\n  rm -rf \"$TMUX_TPM_DIR\"\n  git clone https://github.com/tmux-plugins/tpm \"$TMUX_TPM_DIR\"\n  $TMUX_TPM_DIR/bin/install_plugins\n}\n\npre_unlink() {\n  rm -rf \"$TMUX_TPM_DIR\"\n}\n\nclean_conf() {\n  rm -rf \"$TMUX_PLUGIN_DIR\"\n}\n```\n\n- Run `./pkg.sh sync tmux` to see it in action - your tmux conf linked\n  and plugins installed - you're all set.\n- See [tmux](./tmux/pkg.mod.sh) for a real example.\n\n\n```\ntmux/\n├── .tmux.conf\n└── pkg.mod.sh\n```\n\n### Medium conf\n\n- The above tmux is nice. But what if you want to install tmux itself alongside the config as one unit?\n- You can! Use the same steps above. Just override `install`, `uninstall` commands.\n\n\n```bash\nvars() {\n    TMUX_PLUGIN_DIR=\"$HOME/.tmux/plugins\"\n    TMUX_TPM_DIR=\"$TMUX_PLUGIN_DIR/tpm\"\n    LINKS=(\n        .tmux.conf\n        )\n}\n\npost_link() {\n  rm -rf \"$TMUX_TPM_DIR\"\n  git clone https://github.com/tmux-plugins/tpm \"$TMUX_TPM_DIR\"\n  $TMUX_TPM_DIR/bin/install_plugins\n}\n\npre_unlink() {\n  rm -rf \"$TMUX_TPM_DIR\"\n}\n\nclean_conf() {\n  rm -rf \"$TMUX_PLUGIN_DIR\"\n}\n\ncheck_install() {\n    command -v tmux \u003e /dev/null\n}\n\ninstall() {\n  sudo apt install tmux\n}\n\nuninstall() {\n  sudo apt purge tmux --autoremove\n}\n```\n\n- Run `./pkg.sh sync tmux` to link and setup tpm. tmux will be installed if needed,\n  your tmux conf linked and plugins installed. Now you're really set.\n- This is handy so you don't need to worry about disconnect between your conf\n  and what's actually installed - if conf is there, so is your runtime - in one go.\n- See [tmux](./tmux/pkg.mod.sh) for a real example.\n\n### Alt conf\n\n- This is nice. The first dot repo you get into any machine, but can I use this to install\n  and setup tools base tools like deno or rust that need DRY but isn't the distro repo?\n- Yep! Just override `install`, `uninstall` and leave `LINKS` to default of `()`.\n\n#### Deno\n\n```bash\nvars() {\n    BASH_COMPLETIONS_DIR=$HOME/.bashrc.d/completions\n}\n\ncheck_install() {\n    command -v deno \u003e /dev/null\n}\n\ninstall() {\n    curl -fsSL https://deno.land/x/install/install.sh | sh\n    after_install\n}\n\nafter_install() {\n    deno completions bash \u003e \"$BASH_COMPLETIONS_DIR/deno\"\n}\n\nuninstall() {\n    rm -f \"$BASH_COMPLETIONS_DIR/deno\"\n    rm -rf \"$HOME/.deno\"\n}\n```\n\n- Run `./pkg.sh sync deno` and you're all set.\n- See [deno](./deno/pkg.mod.sh) for a real example.\n\n#### Rust\n\nHere's another for Rust toolchain with rustup:\n\n```bash\nvars() {\n    BASH_COMPLETIONS_DIR=$HOME/.bashrc.d/completions\n    COMPLETION_BINS=(\n        rustup\n        cargo\n    )\n}\n\ncheck_install() {\n    command -v rustup \u003e /dev/null\n}\n\ninstall() {\n    # for all options:\n    # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --help\n    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --no-modify-path -y\n    after_install\n}\n\nafter_install() {\n    for bin in \"${COMPLETION_BINS[@]}\"; do\n        rustup completions bash $bin \u003e \"$BASH_COMPLETIONS_DIR/$bin\"\n    done\n\n    rustup component add rust-analyzer\n    rustup target add wasm32-unknown-unknown\n    rustup toolchain add nightly\n}\n\nuninstall() {\n    # remove completions\n    for bin in \"${COMPLETION_BINS[@]}\"; do\n        rm -f \"$BASH_COMPLETIONS_DIR/$bin\"\n    done\n\n    rustup self uninstall -y\n    rm -rf \"$HOME/.rustup\"\n}\n\npurge() {\n    rm -rf \"$HOME/.cargo\"\n}\n```\n\n- Run `./pkg.sh sync rust` and you're all set.\n- See [rust](./rust/pkg.mod.sh) for a real example.\n\n## Under the hood\n\nThe entire working is probably best explained by reading the tiny `./pkg.sh` file.\nIf you don't have time, these lines are the key logic - that's it.\n\n```bash\n    local x\n    for x in \"${pkgs[@]}\"; do\n        echo \"=== $x ===\"\n        pushd \"./$x\" \u003e/dev/null\n        (\n            # Note that we run all of these in a subshell giving it's mod file the\n            # ability to override anything from this file and start fresh again.\n            source \"./$pkg_mod_file\" 2\u003e/dev/null || true\n            run_cmd \"$cmd\"\n        )\n        popd \u003e/dev/null\n    done\n```\n\n- Everything else is just sane default impls, safe link guards and intuitive lifecycle.\n- Most of the script is comments and help text. The actual logic is minimal convention\n  plumbing - you can use this for anything without having to learn a new tool\n  or language or configuration.\n- The key trick is using subshells for each pkg to make bash behave like a\n  it has nice default impls in pkg.sh while allowing you to override everything and\n  keeping surface area small and intuitive.\n\n### Process\n\n- Create a new pkg dir in the root (e.g., tmux), with whichever files (.tmux.conf).\n- For optional additional config, create `pkg.mod.sh` file inside the pkg dir\n  - See lifecycle section below on flow.\n  - The name of this file is configurable (`PKG_MOD_SH`)\n- Run with `./pkg.sh \u003ccommand\u003e \u003cpackage1\u003e [\u003cpackage2\u003e ...]` to execute commands for\n  specific packages.\n- You can specify multiple packages to operate on in a single command.\n\n### Lifecycle\n\n- The main `pkg.sh` file does the following:\n  - For each specified package:\n    - Sources `pkg.mod.sh` file if available\n    - Runs `vars` so you can override them all.\n    - Runs the specified command (e.g., `sync`, `link`, `install`, `clean` etc.)\n  - Default impl for all of the commands exist in `pkg.sh`\n    - Default impl for `vars`:\n      - `LINKS=()` (nothing will get linked)\n    - Default impl of `link|unlink`\n      - Call `pre_link | pre_unlink` if exists\n      - For each file in `LINKS` var, link them inside `TARGET` dir\n      - Call `post_link | post_unlink` if exists\n      - This allows your package to flexibly choose whichever model:\n      - Just add `LINKS` in `vars` to autolink and use `pre_` and `post_`\n          hooks for additional work\n        - Or override `link` and `unlink` completely and choose your own mechanism\n          entirely.\n  - This allows you to have a simple and consistent way to manage your dotfiles\n    while allowing each package to override all of the impl if default convention\n    isn't enough.\n\n### Misc\n\n- `install|uninstall`:\n  - This extends the capability to also use a package manager to\n    install what's needed before deploying the config, so everything is contained in\n    one set. Allows you to simply deploy whole packages or nothing on different hosts.\n  - **Please note that this is not meant to be yet another manager for your distro**.\n    It's just handy to have this so you can use any source you want. What if your\n    preferred way to get nvim is from source or `linuxbrew` or if you're like me and\n    prefer to install latest version from `nix` than your operating system.\n  - **Can this be made platform agnostic?** Right now it all uses apt since\n    Debian is what I use and haven't bothered to put in the effort to add `APT_PKGS`\n    and `DNF_PKGS` or `BREW_PKGS`. But should be trivial to extend default `install`\n    impl to support this the same way `link` hooks use a default impl and offer\n    `after` and `before` hooks for flexibility.\n- **Host specific config:** It's just a simple script that takes pkgs as input. Create\n  new files with as many sets as needed. See (`xset` dir for example)\n- **Templating**: I prefer my dot file management to be simple, so it is agnostic\n  of how to template. Use the above pre/post install hooks to use any templating\n  engine for each pkg as desired. Low cognitive overhead and flexible to allow\n  different templating based on a pkg's need than dump one on you.\n\n\n### Usage\n\n```\nusage: \u003ccommand\u003e \u003cpackage1\u003e [\u003cpackage2\u003e ...]\nenv:\n  TARGET: (target to install to, defaults to HOME env)\n  PKG_MOD_SH: (the mod sh file to use for each module)\n  FORCE_RELINK: (force relink without requiring unlink or fail for safety)\ncommands:\n  sync: brings everything up to date: check_install [install], [re]link\n  install: install the package\n  check_install: check if package requires installation\n  uninstall: uninstall the package\n  link: link the package, can fail if links already exist\n  relink: force re-link the package\n  unlink: unlink the package\n  clean_conf: clean the package configuration\n  clean: unlink, clean_conf and cleans other files\n  purge: uninstall, clean and purge other files\n```\n\nExample: `./pkg.sh sync bash tmux rust deno` will make sure all these pkgs\nare installed, configured and ready to go!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprasannavl%2Fdotfiles","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprasannavl%2Fdotfiles","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprasannavl%2Fdotfiles/lists"}