diff --git a/nodes/garage-2.json b/nodes/garage-2.json index 42994c7..dba7cfe 100644 --- a/nodes/garage-2.json +++ b/nodes/garage-2.json @@ -36,6 +36,7 @@ "kosmos-base::systemd_emails", "apt::unattended-upgrades", "kosmos-base::firewall", + "kosmos-base::tmux", "kosmos-postfix::default", "postfix::default", "postfix::_common", diff --git a/site-cookbooks/kosmos-base/attributes/default.rb b/site-cookbooks/kosmos-base/attributes/default.rb index 37d8e43..8d0c70f 100644 --- a/site-cookbooks/kosmos-base/attributes/default.rb +++ b/site-cookbooks/kosmos-base/attributes/default.rb @@ -1,2 +1,15 @@ node.default["kosmos-base"]["journald"]["system_max_use"] = "256M" node.default["kosmos-base"]["journald"]["max_retention_sec"] = "7d" + +node.default["kosmos-base"]["tmux"]["plugins_dir"] = "/usr/local/share/tmux/plugins" +node.default["kosmos-base"]["tmux"]["default_conf"] = "/usr/local/share/tmux/tmux.conf.default" +node.default["kosmos-base"]["tmux"]["tpm_redux"]["repo"] = "https://github.com/RyanMacG/tpm-redux.git" +node.default["kosmos-base"]["tmux"]["tpm_redux"]["revision"] = "c5347f71a291eadf26dbecacc43154ecd8ad061b" +# Keys are tpm shorthand names (used in `set -g @plugin`). Values contain the +# git repo and the exact commit SHA the plugin is pinned to. +node.default["kosmos-base"]["tmux"]["plugins"] = { + "tmux-plugins/tmux-resurrect" => { + "repo" => "https://github.com/tmux-plugins/tmux-resurrect.git", + "revision" => "e87d7d592cac97fa38c12395ebec042c154a1844", + }, +} diff --git a/site-cookbooks/kosmos-base/files/default/tmux_users/basti.conf b/site-cookbooks/kosmos-base/files/default/tmux_users/basti.conf new file mode 100644 index 0000000..cf122cd --- /dev/null +++ b/site-cookbooks/kosmos-base/files/default/tmux_users/basti.conf @@ -0,0 +1,44 @@ +# basti's tmux overrides — appended after the system default config is sourced. + +set -g prefix C-a +set -sg escape-time 1 +set -g base-index 1 +set -g history-limit 20000 +set -g mouse off +set -g status-justify centre +set -g status-style dim +set -ag status-style fg=white +set -ag status-style bg=colour236 +set -g window-status-current-style bright +set -ag window-status-current-style fg=white +set -ag window-status-current-style bg=colour238 +set -g pane-border-style fg=colour238 +set -g pane-active-border-style fg=colour244 + +setw -g pane-base-index 1 +setw -g mode-keys vi + +bind C-a send-prefix +bind r source-file ~/.tmux.conf \; display "Reloaded!" +bind | split-window -h +bind - split-window -v +bind h select-pane -L +bind j select-pane -D +bind k select-pane -U +bind l select-pane -R +bind -r H resize-pane -L 5 +bind -r J resize-pane -D 5 +bind -r K resize-pane -U 5 +bind -r L resize-pane -R 5 + +unbind p +bind p paste-buffer +bind-key -T copy-mode-vi v send-keys -X begin-selection +bind-key -T copy-mode-vi y send-keys -X copy-selection-and-cancel + +unbind Up +bind Up new-window -d -n tmp \; swap-pane -s tmp.1 \; select-window -t tmp +unbind Down +bind Down last-window \; swap-pane -s tmp.1 \; kill-window -t tmp + +bind P pipe-pane -o "cat >>~/#W.log" \; display "Toggled logging to ~/#W.log" diff --git a/site-cookbooks/kosmos-base/recipes/default.rb b/site-cookbooks/kosmos-base/recipes/default.rb index 5c6fa78..653e589 100644 --- a/site-cookbooks/kosmos-base/recipes/default.rb +++ b/site-cookbooks/kosmos-base/recipes/default.rb @@ -107,6 +107,8 @@ unless node.chef_environment == "development" include_recipe "kosmos-base::firewall" + include_recipe "kosmos-base::tmux" + include_recipe "kosmos-postfix" node.override["set_fqdn"] = "*" diff --git a/site-cookbooks/kosmos-base/recipes/tmux.rb b/site-cookbooks/kosmos-base/recipes/tmux.rb new file mode 100644 index 0000000..a4b9c39 --- /dev/null +++ b/site-cookbooks/kosmos-base/recipes/tmux.rb @@ -0,0 +1,102 @@ +# +# Cookbook Name:: kosmos-base +# Recipe:: tmux +# + +package "tmux" + +plugins_dir = node["kosmos-base"]["tmux"]["plugins_dir"] +default_conf = node["kosmos-base"]["tmux"]["default_conf"] +tpm_redux = node["kosmos-base"]["tmux"]["tpm_redux"] +plugins = node["kosmos-base"]["tmux"]["plugins"] + +directory plugins_dir do + owner "root" + group "root" + mode "0755" + recursive true + action :create +end + +# Install tpm-redux (the plugin manager) at a pinned commit. +git "#{plugins_dir}/tpm-redux" do + repository tpm_redux["repo"] + revision tpm_redux["revision"] + user "root" + group "root" + action :sync +end + +# Install each managed plugin at a pinned commit. Plugins are cloned by Chef +# into a shared, root-owned directory so that users never need to install them +# themselves. +plugins.each do |shorthand, meta| + git "#{plugins_dir}/#{shorthand.split('/').last}" do + repository meta["repo"] + revision meta["revision"] + user "root" + group "root" + action :sync + end +end + +# System default tmux config: minimal — only the plugin manager environment, +# the @plugin declarations for the chef-managed plugins, and the tpm-redux +# initialisation line (which must be the last line of the file). +template default_conf do + source "tmux.conf.default.erb" + owner "root" + group "root" + mode "0644" + variables( + plugins_dir: plugins_dir, + plugins: plugins + ) + action :create +end + +# Write a per-user ~/.tmux.conf. Users who submit their own overrides as a +# cookbook file at files/default/tmux_users/.conf get a fully +# managed config: it sources the system default (so chef-managed plugins +# always load) and then appends the user's overrides. Users who have not +# submitted a file get a blank importer written only if ~/.tmux.conf does +# not already exist, so any hand-written/local config is left untouched. +cookbook = run_context.cookbook_collection["kosmos-base"] + +search(:users, "groups:sysadmin AND NOT action:remove") do |u| + username = u["username"] || u["id"] + home_dir = u["home"] || "/home/#{username}" + group_name = u["gid"] || username + + next unless Dir.exist?(home_dir) + + user_conf_source = "tmux_users/#{username}.conf" + + if cookbook.has_cookbook_file_for_node?(node, user_conf_source) + disk_path = cookbook.preferred_filename_on_disk_location(node, :files, user_conf_source) + # Chef reads ERB templates with IO.binread, so the template output buffer + # is ASCII-8BIT. File.read returns UTF-8 on the node; concatenating a UTF-8 + # string into an ASCII-8BIT buffer raises an encoding error on non-ASCII + # content. Retag as ASCII-8BIT to match the buffer — the raw bytes are + # still valid UTF-8, so the written file is correct. + user_content = File.read(disk_path).force_encoding("ASCII-8BIT") + + template "#{home_dir}/.tmux.conf" do + source "tmux.conf.user.erb" + owner username + group group_name + mode "0644" + variables(default_conf: default_conf, user_content: user_content) + action :create + end + else + template "#{home_dir}/.tmux.conf" do + source "tmux.conf.user.erb" + owner username + group group_name + mode "0644" + variables(default_conf: default_conf, user_content: nil) + action :create_if_missing + end + end +end diff --git a/site-cookbooks/kosmos-base/templates/default/tmux.conf.default.erb b/site-cookbooks/kosmos-base/templates/default/tmux.conf.default.erb new file mode 100644 index 0000000..c14615c --- /dev/null +++ b/site-cookbooks/kosmos-base/templates/default/tmux.conf.default.erb @@ -0,0 +1,14 @@ +# Managed by Chef (kosmos-base::tmux). Do not edit on this host. +# +# This is the system default tmux config. It is sourced by each user's +# ~/.tmux.conf via `source-file`. Plugins are managed by Chef and live in +# a shared, root-owned directory; users do not install plugins themselves. + +set-environment -g TMUX_PLUGIN_MANAGER_PATH '<%= @plugins_dir %>' + +<% @plugins.each do |shorthand, _meta| -%> +set -g @plugin '<%= shorthand %>' +<% end -%> + +# Initialize TPM Redux (must remain the last line of the file) +run '<%= @plugins_dir %>/tpm-redux/tpm' diff --git a/site-cookbooks/kosmos-base/templates/default/tmux.conf.user.erb b/site-cookbooks/kosmos-base/templates/default/tmux.conf.user.erb new file mode 100644 index 0000000..3b05773 --- /dev/null +++ b/site-cookbooks/kosmos-base/templates/default/tmux.conf.user.erb @@ -0,0 +1,15 @@ +# ~/.tmux.conf — created/managed by Chef (kosmos-base::tmux). +# +# This file imports the system default config below, then appends any overrides +# the user has submitted to files/default/tmux_users/.conf in the +# kosmos-base cookbook. If you manage your own tmux config locally and have not +# submitted a file, Chef will not overwrite it. + +source-file <%= @default_conf %> + +<% if @user_content && !@user_content.empty? -%> +# === user overrides (from files/default/tmux_users/.conf) === +<%= @user_content %> +<% else -%> +# === Add your custom settings below this line === +<% end -%>