From f04d7d2808bc56e67a8b5ae32385b5d132dea496 Mon Sep 17 00:00:00 2001 From: 35C4n0r <70096901+35C4n0r@users.noreply.github.com> Date: Fri, 25 Jul 2025 07:01:57 +0530 Subject: [PATCH] feat: tmux module (#229) Closes #203 /claim #203 ## Description Introduce the `tmux` module ## Demo https://www.loom.com/share/ec8169d34c3043f7af2163b1a1a14a4b?sid=1ea8bcb2-3db0-43ca-965a-5ed42eec3448 ## Type of Change - [x] New module - [ ] Bug fix - [ ] Feature/enhancement - [ ] Documentation - [ ] Other ## Module Information **Path:** `registry/anomaly/modules/tmux` **New version:** `v1.0.0` **Breaking change:** [ ] Yes [x] No ## Testing & Validation - [x] Tests pass (`bun test`) - [x] Code formatted (`bun run fmt`) - [x] Changes tested locally ## Related Issues #203 --- .gitignore | 3 + .icons/tmux.svg | 1 + registry/anomaly/.images/avatar.jpeg | Bin 0 -> 9064 bytes registry/anomaly/README.md | 13 ++ registry/anomaly/modules/tmux/README.md | 101 ++++++++++++ registry/anomaly/modules/tmux/main.test.ts | 35 ++++ registry/anomaly/modules/tmux/main.tf | 78 +++++++++ registry/anomaly/modules/tmux/scripts/run.sh | 153 ++++++++++++++++++ .../anomaly/modules/tmux/scripts/start.sh | 37 +++++ 9 files changed, 421 insertions(+) create mode 100644 .icons/tmux.svg create mode 100644 registry/anomaly/.images/avatar.jpeg create mode 100644 registry/anomaly/README.md create mode 100644 registry/anomaly/modules/tmux/README.md create mode 100644 registry/anomaly/modules/tmux/main.test.ts create mode 100644 registry/anomaly/modules/tmux/main.tf create mode 100755 registry/anomaly/modules/tmux/scripts/run.sh create mode 100755 registry/anomaly/modules/tmux/scripts/start.sh diff --git a/.gitignore b/.gitignore index 157c642a..55947fc5 100644 --- a/.gitignore +++ b/.gitignore @@ -145,3 +145,6 @@ dist # Generated credentials from google-github-actions/auth gha-creds-*.json + +# IDEs +.idea diff --git a/.icons/tmux.svg b/.icons/tmux.svg new file mode 100644 index 00000000..ac0174ed --- /dev/null +++ b/.icons/tmux.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/registry/anomaly/.images/avatar.jpeg b/registry/anomaly/.images/avatar.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ca1072d8dce0807e1688f16be0e3bddfa5d29ef7 GIT binary patch literal 9064 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 [!IMPORTANT] +> +> - If you provide a custom `tmux_config`, it will completely replace the default configuration. Ensure you include plugin +> and TPM initialization lines if you want plugin support and session persistence. +> - The script will attempt to install dependencies using `sudo` where required. +> - If `git` is not installed, TPM installation will fail. +> - If you are using custom config, you'll be responsible for setting up persistence and plugins. +> - The `order`, `group`, and `icon` variables allow you to customize how tmux apps appear in the Coder UI. +> - In case of session restart or shh reconnection, the tmux session will be automatically restored :) diff --git a/registry/anomaly/modules/tmux/main.test.ts b/registry/anomaly/modules/tmux/main.test.ts new file mode 100644 index 00000000..802147db --- /dev/null +++ b/registry/anomaly/modules/tmux/main.test.ts @@ -0,0 +1,35 @@ +import { describe, it, expect } from "bun:test"; +import { + runTerraformApply, + runTerraformInit, + testRequiredVariables, + findResourceInstance, +} from "~test"; +import path from "path"; + +const moduleDir = path.resolve(__dirname); + +const requiredVars = { + agent_id: "dummy-agent-id", +}; + +describe("tmux module", async () => { + await runTerraformInit(moduleDir); + + // 1. Required variables + testRequiredVariables(moduleDir, requiredVars); + + // 2. coder_script resource is created + it("creates coder_script resource", async () => { + const state = await runTerraformApply(moduleDir, requiredVars); + const scriptResource = findResourceInstance(state, "coder_script"); + expect(scriptResource).toBeDefined(); + expect(scriptResource.agent_id).toBe(requiredVars.agent_id); + + // check that the script contains expected lines + expect(scriptResource.script).toContain("Installing tmux"); + expect(scriptResource.script).toContain("Installing Tmux Plugin Manager (TPM)"); + expect(scriptResource.script).toContain("tmux configuration created at"); + expect(scriptResource.script).toContain("✅ tmux setup complete!"); + }); +}); diff --git a/registry/anomaly/modules/tmux/main.tf b/registry/anomaly/modules/tmux/main.tf new file mode 100644 index 00000000..36f8471f --- /dev/null +++ b/registry/anomaly/modules/tmux/main.tf @@ -0,0 +1,78 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 2.5" + } + } +} + +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +variable "tmux_config" { + type = string + description = "Custom tmux configuration to apply." + default = "" +} + +variable "save_interval" { + type = number + description = "Save interval (in minutes)." + default = 1 +} + +variable "order" { + type = number + description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)." + default = null +} + +variable "group" { + type = string + description = "The name of a group that this app belongs to." + default = null +} + +variable "icon" { + type = string + description = "The icon to use for the app." + default = "/icon/tmux.svg" +} + +variable "sessions" { + type = list(string) + description = "List of tmux sessions to create or start." + default = ["default"] +} + +resource "coder_script" "tmux" { + agent_id = var.agent_id + display_name = "tmux" + icon = "/icon/terminal.svg" + script = templatefile("${path.module}/scripts/run.sh", { + TMUX_CONFIG = var.tmux_config + SAVE_INTERVAL = var.save_interval + }) + run_on_start = true + run_on_stop = false +} + +resource "coder_app" "tmux_sessions" { + for_each = toset(var.sessions) + + agent_id = var.agent_id + slug = "tmux-${each.value}" + display_name = "tmux - ${each.value}" + icon = var.icon + order = var.order + group = var.group + + command = templatefile("${path.module}/scripts/start.sh", { + SESSION_NAME = each.value + }) +} diff --git a/registry/anomaly/modules/tmux/scripts/run.sh b/registry/anomaly/modules/tmux/scripts/run.sh new file mode 100755 index 00000000..90c0d84b --- /dev/null +++ b/registry/anomaly/modules/tmux/scripts/run.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash + +BOLD='\033[0;1m' + +# Convert templated variables to shell variables +SAVE_INTERVAL="${SAVE_INTERVAL}" +TMUX_CONFIG="${TMUX_CONFIG}" + +# Function to install tmux +install_tmux() { + printf "Checking for tmux installation\n" + + if command -v tmux &> /dev/null; then + printf "tmux is already installed \n\n" + return 0 + fi + + printf "Installing tmux \n\n" + + # Detect package manager and install tmux + if command -v apt-get &> /dev/null; then + sudo apt-get update + sudo apt-get install -y tmux + elif command -v yum &> /dev/null; then + sudo yum install -y tmux + elif command -v dnf &> /dev/null; then + sudo dnf install -y tmux + elif command -v zypper &> /dev/null; then + sudo zypper install -y tmux + elif command -v apk &> /dev/null; then + sudo apk add tmux + elif command -v brew &> /dev/null; then + brew install tmux + else + printf "No supported package manager found. Please install tmux manually. \n" + exit 1 + fi + + printf "tmux installed successfully \n" +} + +# Function to install Tmux Plugin Manager (TPM) +install_tpm() { + local tpm_dir="$HOME/.tmux/plugins/tpm" + + if [ -d "$tpm_dir" ]; then + printf "TPM is already installed" + return 0 + fi + + printf "Installing Tmux Plugin Manager (TPM) \n" + + # Create plugins directory + mkdir -p "$HOME/.tmux/plugins" + + # Clone TPM repository + if command -v git &> /dev/null; then + git clone https://github.com/tmux-plugins/tpm "$tpm_dir" + printf "TPM installed successfully" + else + printf "Git is not installed. Please install git to use tmux plugins. \n" + exit 1 + fi +} + +# Function to create tmux configuration +setup_tmux_config() { + printf "Setting up tmux configuration \n" + + local config_dir="$HOME/.tmux" + local config_file="$HOME/.tmux.conf" + + mkdir -p "$config_dir" + + if [ -n "$TMUX_CONFIG" ]; then + printf "$TMUX_CONFIG" > "$config_file" + printf "$${BOLD}Custom tmux configuration applied at {$config_file} \n\n" + else + cat > "$config_file" << EOF +# Tmux Configuration File + +# ============================================================================= +# PLUGIN CONFIGURATION +# ============================================================================= + +# List of plugins +set -g @plugin 'tmux-plugins/tpm' +set -g @plugin 'tmux-plugins/tmux-sensible' +set -g @plugin 'tmux-plugins/tmux-resurrect' +set -g @plugin 'tmux-plugins/tmux-continuum' + +# tmux-continuum configuration +set -g @continuum-restore 'on' +set -g @continuum-save-interval '$${SAVE_INTERVAL}' +set -g @continuum-boot 'on' +set -g status-right 'Continuum status: #{continuum_status}' + +# ============================================================================= +# KEY BINDINGS FOR SESSION MANAGEMENT +# ============================================================================= + +# Quick session save and restore +bind C-s run-shell "~/.tmux/plugins/tmux-resurrect/scripts/save.sh" +bind C-r run-shell "~/.tmux/plugins/tmux-resurrect/scripts/restore.sh" + +# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf) +run '~/.tmux/plugins/tpm/tpm' +EOF + printf "tmux configuration created at {$config_file} \n\n" + fi +} + +# Function to install tmux plugins +install_plugins() { + printf "Installing tmux plugins" + + # Check if TPM is installed + if [ ! -d "$HOME/.tmux/plugins/tpm" ]; then + printf "TPM is not installed. Cannot install plugins. \n" + return 1 + fi + + # Install plugins using TPM + "$HOME/.tmux/plugins/tpm/bin/install_plugins" + + printf "tmux plugins installed successfully \n" +} + +# Main execution +main() { + printf "$${BOLD} 🛠️Setting up tmux with session persistence! \n\n" + printf "" + + # Install dependencies + install_tmux + install_tpm + + # Setup tmux configuration + setup_tmux_config + + # Install plugins + install_plugins + + printf "$${BOLD}✅ tmux setup complete! \n\n" + + printf "$${BOLD} Attempting to restore sessions\n" + tmux new-session -d \; source-file ~/.tmux.conf \; run-shell '~/.tmux/plugins/tmux-resurrect/scripts/restore.sh' + printf "$${BOLD} Sessions restored: -> %s\n" "$(tmux ls)" + +} + +# Run main function +main \ No newline at end of file diff --git a/registry/anomaly/modules/tmux/scripts/start.sh b/registry/anomaly/modules/tmux/scripts/start.sh new file mode 100755 index 00000000..4638c8f7 --- /dev/null +++ b/registry/anomaly/modules/tmux/scripts/start.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# Convert templated variables to shell variables +SESSION_NAME='${SESSION_NAME}' + +# Function to check if tmux is installed +check_tmux() { + if ! command -v tmux &> /dev/null; then + echo "tmux is not installed. Please run the tmux setup script first." + exit 1 + fi +} + +# Function to handle a single session +handle_session() { + local session_name="$1" + + # Check if the session exists + if tmux has-session -t "$session_name" 2>/dev/null; then + echo "Session '$session_name' exists, attaching to it..." + tmux attach-session -t "$session_name" + else + echo "Session '$session_name' does not exist, creating it..." + tmux new-session -d -s "$session_name" + tmux attach-session -t "$session_name" + fi +} + +# Main function +main() { + # Check if tmux is installed + check_tmux + handle_session "${SESSION_NAME}" +} + +# Run the main function +main