aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2025-07-14 08:41:14 +0200
committerMiquel Sabaté Solà <mikisabate@gmail.com>2025-07-14 08:41:14 +0200
commita999e73e28f0875fc811c62725712d01818e3b59 (patch)
treeef5546e714b29d126ad1f06f158b8c8886ed2822
parent0ee7a0b129facc85063a042d685613cd4f0a1d2e (diff)
downloaddotfiles-a999e73e28f0875fc811c62725712d01818e3b59.tar.gz
dotfiles-a999e73e28f0875fc811c62725712d01818e3b59.zip
bin: Introduce the doctor.rb install/update script
This replaces the good ol' install.sh script, and it allows me to not only install on a new system, but also upgrade dependencies, repair broken links, etc. This is certainly not Nix, but at least it's not as bad as it used to be. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
-rw-r--r--.rubocop.yml3
-rw-r--r--README.md18
-rwxr-xr-xbin/doctor.rb192
3 files changed, 205 insertions, 8 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index e70098a..b1e507d 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,3 +1,6 @@
+AllCops:
+ NewCops: enable
+
# Allow me to write me own name on the copyright notice :)
Style/AsciiComments:
AllowedChars: ['é', 'à']
diff --git a/README.md b/README.md
index b678e6c..3de12cb 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,12 @@
These are my personal dotfiles, use them at your own risk! To install everything
-just run the `install.sh` script.
+and keep it up-to-date, I run `bin/doctor.rb`.
-## License
-
-Every file is licensed under the GPLv3+ unless stated otherwise. I've tried to
-mention every major influence on my `dotfiles`, but most surely I've forgotten
-to add some people. If you feel like you should be mentioned somewhere in my
-config files, please send me an email. You can find the full license in the
-`LICENSE` file and below:
+Every file is licensed under the
+[GPLv3+](https://www.gnu.org/licenses/gpl-3.0.html) unless stated
+otherwise. I've tried to mention every major influence but most surely I've
+forgotten to add some people. If you feel like you should be mentioned somewhere
+in my config files, please send me an email. You can find the full license in
+the `LICENSE` file and below:
```
Copyright (C) 2014-Ω Miquel Sabaté Solà <mikisabate@gmail.com>
@@ -24,3 +23,6 @@ PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
```
+
+And cheers to the many people who have contributed to free software and that
+have ventured to share their knowledge and expertise selflessly. Thanks!
diff --git a/bin/doctor.rb b/bin/doctor.rb
new file mode 100755
index 0000000..3b0093a
--- /dev/null
+++ b/bin/doctor.rb
@@ -0,0 +1,192 @@
+# frozen_string_literal: true
+
+# Copyright (C) 2025-Ω Miquel Sabaté Solà <mikisabate@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+require 'fileutils'
+require 'pathname'
+
+##
+# Early checks.
+
+raise 'Only Linux on x86 supported!' if RUBY_PLATFORM != 'x86_64-linux'
+raise 'Only CRuby, sorry!' if RUBY_ENGINE != 'ruby'
+
+##
+# Util functions.
+
+def has?(executable:)
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
+
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
+ exts.each do |ext|
+ exe = File.join(path, "#{executable}#{ext}")
+ return exe if File.executable?(exe) && !File.directory?(exe)
+ end
+ end
+
+ false
+end
+
+##
+# Mandatory binaries.
+
+puts '0. Checking on binaries needed for the system.'
+
+pending = []
+%w[git curl emacs vim mise go cargo ruby rg].each do |bin|
+ pending << bin unless has?(executable: bin)
+end
+
+if pending.size.positive?
+ puts "You need to install: #{pending.join(', ')}."
+ exit 0
+end
+
+##
+# Symbolic links and all that jazz.
+
+puts '1. Creating symlinks.'
+
+raise 'Dude, something is really funky on your HOME!' unless Dir.exist?(Dir.home)
+
+SOURCE = Pathname.new("#{File.dirname(__FILE__)}/..").realpath
+
+files = %w[
+ bin .vim
+ .gnupg/gpg-agent.conf .gnupg/gpg.conf
+ .config/alacritty .config/emacs .config/gdb .config/ghostty .config/irb
+ .config/mise .config/msmtp .config/mimeapps.list .config/user-dirs.dirs
+ .bash_profile .bashrc .clang-format .gemrc .gitconfig .gitconfig-suse
+ .hunspell_ca_ES .inputrc .mbsyncrc .psqlrc .rvmrc .tmux.conf .valgrindrc .vimrc
+]
+
+files.each do |f|
+ dst = File.join(Dir.home, f)
+ FileUtils.rm_f(dst)
+
+ src = File.join(SOURCE, f)
+ File.symlink(src, dst)
+end
+
+# The global .gitignore needs a rename.
+dst = File.join(Dir.home, '.gitignore')
+FileUtils.rm_f(dst)
+File.symlink(File.join(SOURCE, '.global.gitignore'), dst)
+
+##
+# Stuff I'm wildly downloading from the internet.
+
+puts "2. Downloading stuff from the internet."
+
+src_home = File.join(Dir.home, 'src/github.com/mssola')
+
+# The 'g' utility.
+if File.exist?(File.join(src_home, 'g'))
+ Dir.chdir(File.join(src_home, 'g'))
+ puts ">> pulling github.com/mssola/g..."
+ system('git pull --rebase')
+else
+ Dir.chdir(src_home)
+ puts ">> cloning github.com/mssola/g..."
+ system('git clone git@github.com:mssola/g.git')
+end
+
+# And link the relevant files from 'g'.
+%w[g.sh gcompletion.sh].each do |file|
+ dst = File.join(Dir.home, ".#{file}")
+ FileUtils.rm_f(dst) if File.exist?(dst)
+ File.symlink(File.join(src_home, "g/#{file}"), dst)
+end
+
+# The 'soria' theme for GNU Emacs.
+if File.exist?(File.join(src_home, 'soria'))
+ Dir.chdir(File.join(src_home, 'soria'))
+ puts ">> pulling github.com/mssola/soria..."
+ system('git pull --rebase')
+else
+ Dir.chdir(src_home)
+ puts ">> cloning github.com/mssola/soria..."
+ system('git clone git@github.com:mssola/soria.git')
+end
+
+# git-prompt
+puts ">> downloading git-prompt.sh from git/contrib..."
+dst = File.join(Dir.home, '.git-prompt.sh')
+FileUtils.rm_f(dst) if File.exist?(dst)
+system("curl -o #{dst} https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh")
+puts ''
+
+##
+# Dependencies from programming languages.
+
+puts "3. Installing/updating dependencies from programming languages."
+
+system('go install golang.org/x/tools/gopls@latest')
+system('go install golang.org/x/tools/cmd/goimports@latest')
+
+system('gem install rubocop')
+system('gem install solargraph')
+
+##
+# Optional stuff.
+
+extra = []
+unless has?(executable: 'delta')
+ extra << 'git-delta'
+ puts "[!!] 'git-delta' is not installed: 'magit-delta' won't work!"
+end
+
+unless has?(executable: 'mu')
+ extra << 'maildir-utils'
+ puts "[!!] 'mu/mu4e' is not installed: email won't work!"
+end
+
+unless has?(executable: 'mbsync')
+ extra << 'isync'
+ puts "[!!] 'mbsync' is not installed: fetching emails won't work!"
+end
+
+unless has?(executable: 'difft')
+ extra << 'difftastic'
+ puts "[!!] 'difftastic' is not installed: git won't look as awesome!"
+end
+
+git_send_email = "/usr/libexec/git/git-send-email"
+if !File.exist?(git_send_email) || !File.executable?(git_send_email)
+ extra << 'git-email'
+ puts "[!!] 'git send-email' is not installed: you won't be able to send patches via email!"
+end
+
+if extra.size.positive?
+ print "\n=> You can install all of that with:\n"
+ puts " => zypper in #{extra.join(' ')}"
+end
+
+##
+# Binaries which need to go global.
+
+extra = []
+(Dir.glob(SOURCE.join('bin/zypper-*')) << SOURCE.join('bin/e')).map do |src|
+ dst = "/usr/bin/#{File.basename(src)}"
+ extra << "ln -s #{src} #{dst}" unless File.exist?(dst)
+end
+
+if extra.size.positive?
+ print "\n=> Extra work: run the following while being *root*.\n\n"
+ puts extra.join("\n")
+end
+
+puts "Done!"