-
Notifications
You must be signed in to change notification settings - Fork 39
feat: tmux module #229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
feat: tmux module #229
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
b66aead
feat: basic tmux module
35C4n0r 3e894dc
fix: echo statements
35C4n0r b3680df
feat: replace echo with printf
35C4n0r 9d735c8
feat: add test
35C4n0r b27641f
feat: update readme and format
35C4n0r f36ce27
feat: update readme
35C4n0r 25857a4
Merge branch 'main' into feat-tmux
35C4n0r 8b05a5d
feat: update readme
35C4n0r c031c3b
Merge branch 'main' into feat-tmux
35C4n0r dac1878
ci: bun fmt
35C4n0r 200d922
Merge remote-tracking branch 'origin/feat-tmux' into feat-tmux
35C4n0r a06d185
Merge branch 'main' into feat-tmux
35C4n0r 427586d
feat: add frontmatter
35C4n0r cf8014f
feat: add tmux svg
35C4n0r 26e4c12
Merge branch 'main' into feat-tmux
35C4n0r d9f8c95
feat: update readme
35C4n0r 0c243cc
feat: start.sh
35C4n0r a1b4848
feat: enhance tmux session management and configuration
35C4n0r 6d0c19d
feat: add multi-session support for tmux in Coder UI
35C4n0r 306bec1
Merge branch 'main' into feat-tmux
35C4n0r 9f32bd5
docs: change NOTE to IMPORTANT and add tmux icon
35C4n0r 8c0e7b3
Merge branch 'main' into feat-tmux
35C4n0r c82fe73
docs: update README with module source and example for tmux
35C4n0r 41c8636
fix: add versions
35C4n0r fddc6ea
fix: fix README.md
35C4n0r File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -145,3 +145,6 @@ dist | |
|
||
# Generated credentials from google-github-actions/auth | ||
gha-creds-*.json | ||
|
||
# IDEs | ||
.idea | ||
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- | ||
display_name: "Jay Kumar" | ||
bio: "I'm a Software Engineer :)" | ||
avatar_url: "./.images/avatar.png" | ||
github: "35C4n0r" | ||
linkedin: "https://www.linkedin.com/in/jaykum4r" | ||
support_email: "[email protected]" | ||
status: "community" | ||
--- | ||
|
||
# Your Name | ||
|
||
I'm a Software Engineer :) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
--- | ||
display_name: "Tmux" | ||
description: "Tmux for coder agent :)" | ||
icon: "../../../../.icons/tmux.svg" | ||
verified: false | ||
tags: ["tmux", "terminal", "persistent"] | ||
--- | ||
|
||
# tmux | ||
|
||
This module provisions and configures [tmux](https://github.com/tmux/tmux) with session persistence and plugin support | ||
for a Coder agent. It automatically installs tmux, the Tmux Plugin Manager (TPM), and a set of useful plugins, and sets | ||
up a default or custom tmux configuration with session save/restore capabilities. | ||
|
||
```tf | ||
module "tmux" { | ||
source = "registry.coder.com/anomaly/tmux/coder" | ||
version = "1.0.0" | ||
agent_id = coder_agent.example.id | ||
} | ||
``` | ||
|
||
## Features | ||
|
||
- Installs tmux if not already present | ||
- Installs TPM (Tmux Plugin Manager) | ||
- Configures tmux with plugins for sensible defaults, session persistence, and automation: | ||
- `tmux-plugins/tpm` | ||
- `tmux-plugins/tmux-sensible` | ||
- `tmux-plugins/tmux-resurrect` | ||
- `tmux-plugins/tmux-continuum` | ||
- Supports custom tmux configuration | ||
- Enables automatic session save | ||
- Configurable save interval | ||
- **Supports multiple named tmux sessions, each as a separate app in the Coder UI** | ||
|
||
## Usage | ||
|
||
```tf | ||
module "tmux" { | ||
source = "registry.coder.com/anomaly/tmux/coder" | ||
version = "1.0.0" | ||
agent_id = coder_agent.example.id | ||
tmux_config = "" # Optional: custom tmux.conf content | ||
save_interval = 1 # Optional: save interval in minutes | ||
sessions = ["default", "dev", "ops"] # Optional: list of tmux sessions | ||
order = 1 # Optional: UI order | ||
group = "Terminal" # Optional: UI group | ||
icon = "/icon/tmux.svg" # Optional: app icon | ||
} | ||
``` | ||
|
||
## Multi-Session Support | ||
|
||
This module can provision multiple tmux sessions, each as a separate app in the Coder UI. Use the `sessions` variable to specify a list of session names. For each session, a `coder_app` is created, allowing you to launch or attach to that session directly from the UI. | ||
|
||
- **sessions**: List of tmux session names (default: `["default"]`). | ||
|
||
## How It Works | ||
|
||
- **tmux Installation:** | ||
- Checks if tmux is installed; if not, installs it using the system's package manager (supports apt, yum, dnf, | ||
zypper, apk, brew). | ||
- **TPM Installation:** | ||
- Installs the Tmux Plugin Manager (TPM) to `~/.tmux/plugins/tpm` if not already present. | ||
- **tmux Configuration:** | ||
- If `tmux_config` is provided, writes it to `~/.tmux.conf`. | ||
- Otherwise, generates a default configuration with plugin support and session persistence (using tmux-resurrect and | ||
tmux-continuum). | ||
- Sets up key bindings for quick session save (`Ctrl+s`) and restore (`Ctrl+r`). | ||
- **Plugin Installation:** | ||
- Installs plugins via TPM. | ||
- **Session Persistence:** | ||
- Enables automatic session save/restore at the configured interval. | ||
|
||
## Example | ||
|
||
```tf | ||
module "tmux" { | ||
source = "registry.coder.com/anomaly/tmux/coder" | ||
version = "1.0.0" | ||
agent_id = var.agent_id | ||
sessions = ["default", "dev", "anomaly"] | ||
tmux_config = <<-EOT | ||
set -g mouse on | ||
set -g history-limit 10000 | ||
EOT | ||
group = "Terminal" | ||
order = 2 | ||
} | ||
``` | ||
|
||
> [!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 :) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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!"); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
}) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.