Last active
June 30, 2025 13:04
-
-
Save monotykamary/09ee6dc0031db66c63aa58cdcd214191 to your computer and use it in GitHub Desktop.
Calling devpod binaries as if they were on your machine
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
#!/usr/bin/env bash | |
# Requires direnv: https://direnv.net/ | |
# Check if this is a devcontainer project | |
if [[ ! -f .devcontainer/devcontainer.json ]] && [[ ! -f devcontainer.json ]]; then | |
return # Not a devcontainer project, do nothing | |
fi | |
if ! command -v devpod >/dev/null 2>&1; then | |
return # devpod not available, do nothing | |
fi | |
log_status "Setting up devcontainer command wrappers..." | |
# Get project name for devpod | |
PROJECT_NAME=$(basename "$(pwd)") | |
# Environment variables to forward to devcontainer | |
ENV_VARS_TO_FORWARD=( | |
RAILS_ENV | |
NODE_ENV | |
RACK_ENV | |
APP_ENV | |
ENVIRONMENT | |
DEBUG | |
VERBOSE | |
VISUAL | |
EDITOR | |
PAGER | |
) | |
# Commands to wrap (will also auto-discover from bin/) | |
COMMANDS_TO_WRAP=( | |
ruby | |
yarn | |
bundle | |
rails | |
rake | |
rspec | |
rubocop | |
ruby-lsp | |
pnpm | |
make | |
) | |
# Auto-discover executables in bin/ folder (macOS compatible) | |
if [[ -d bin ]]; then | |
while IFS= read -r -d '' file; do | |
if [[ -x "$file" ]]; then | |
COMMANDS_TO_WRAP+=($(basename "$file")) | |
fi | |
done < <(find bin -maxdepth 1 -type f -perm +111 -print0 2>/dev/null) | |
fi | |
# Remove duplicates from commands array | |
readarray -t UNIQUE_COMMANDS < <(printf '%s\n' "${COMMANDS_TO_WRAP[@]}" | sort -u) | |
# Create wrapper functions and add to PATH | |
WRAPPER_DIR="$PWD/.direnv/bin" | |
rm -r "$WRAPPER_DIR" | |
mkdir -p "$WRAPPER_DIR" | |
for cmd in "${UNIQUE_COMMANDS[@]}"; do | |
# Skip if command name contains special characters | |
if [[ ! "$cmd" =~ ^[a-zA-Z0-9_-]+$ ]]; then | |
continue | |
fi | |
# Create wrapper script instead of function | |
cat >"$WRAPPER_DIR/$cmd" <<EOF | |
#!/usr/bin/env bash | |
set -euo pipefail | |
PROJECT_NAME="$PROJECT_NAME" | |
ENV_VARS_TO_FORWARD=(${ENV_VARS_TO_FORWARD[*]}) | |
# Build environment variable exports | |
env_exports="" | |
for var in "\${ENV_VARS_TO_FORWARD[@]}"; do | |
if [[ -n "\${!var:-}" ]]; then | |
env_exports+="export \$var=\$(printf '%q' "\${!var}"); " | |
fi | |
done | |
# Determine which command to run | |
cmd_to_run="" | |
if [[ -x "$PWD/bin/$cmd" ]]; then | |
cmd_to_run="bin/$cmd" | |
else | |
cmd_to_run="$cmd" | |
fi | |
# Build the full command with arguments | |
full_command="\${env_exports}\${cmd_to_run}" | |
for arg in "\$@"; do | |
full_command+=" \$(printf '%q' "\$arg")" | |
done | |
# Execute in devcontainer | |
# Use interactive bash to ensure .bashrc is sourced | |
exec devpod ssh "\$PROJECT_NAME" \\ | |
--silent \\ | |
--workdir '/workspace' \\ | |
--command "bash -i -c '\$full_command'" | |
EOF | |
# Make wrapper executable | |
chmod +x "$WRAPPER_DIR/$cmd" | |
done | |
# Add wrapper directory to PATH (this takes precedence) | |
PATH_add "$WRAPPER_DIR" | |
log_status "Devcontainer wrappers active for: ${UNIQUE_COMMANDS[*]}" | |
log_status "Commands will automatically execute in devcontainer" |
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
setup_devcontainer_wrappers() { | |
local static_commands=("ruby" "node" "npm" "yarn" "python" "pip" "bundle" "rails" "rake" "rspec" "rubocop" "pnpm" "cargo" "go") | |
local bin_commands=() | |
# Add commands from bin/ folder if it exists | |
if [[ -d "bin" ]]; then | |
for file in bin/*; do | |
if [[ -x "$file" && -f "$file" ]]; then | |
local basename_file=$(basename "$file") | |
bin_commands+=("$basename_file") | |
fi | |
done | |
fi | |
# Combine static and dynamic commands, removing duplicates | |
local all_commands=($(printf '%s\n' "${static_commands[@]}" "${bin_commands[@]}" | sort -u)) | |
for cmd in "${all_commands[@]}"; do | |
# Create wrapper function that captures common environment variables | |
eval "${cmd}() { | |
local project_name=\"\$(basename \"\$(pwd)\")\" | |
local current_cmd=\"${cmd}\" | |
# Capture key environment variables | |
local env_prefix=\"\" | |
for var in RAILS_ENV NODE_ENV RACK_ENV APP_ENV ENVIRONMENT DEBUG VERBOSE VISUAL EDITOR PAGER; do | |
if [[ -n \"\${ZSH_VERSION:-}\" ]]; then | |
local var_value=\${(P)var} | |
else | |
local var_value=\${!var} | |
fi | |
if [[ -n \"\$var_value\" ]]; then | |
env_prefix+=\"export \$var='\$var_value'; \" | |
fi | |
done | |
if [[ -f .devcontainer/devcontainer.json ]] || [[ -f devcontainer.json ]]; then | |
# Build command with escaped arguments | |
local full_cmd=\"\${env_prefix}\" | |
if [[ -x \"bin/\${current_cmd}\" ]]; then | |
full_cmd+=\"bin/\${current_cmd}\" | |
else | |
full_cmd+=\"\${current_cmd}\" | |
fi | |
for arg in \"\$@\"; do | |
full_cmd+=\" \$(printf '%q' \"\$arg\")\" | |
done | |
devpod ssh \"\$project_name\" --silent --workdir '/workspace' --command \"bash -l -c \$(printf '%q' \"\$full_cmd\")\" | |
else | |
if [[ -x \"bin/\${current_cmd}\" ]]; then | |
\"bin/\${current_cmd}\" \"\$@\" | |
else | |
command \${current_cmd} \"\$@\" | |
fi | |
fi | |
}" | |
done | |
} | |
# Simplified env wrapper for explicit env command usage | |
env() { | |
local env_vars=() | |
local cmd_and_args=() | |
local found_command=false | |
for arg in "$@"; do | |
if [[ ! $found_command && "$arg" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; then | |
env_vars+=("$arg") | |
else | |
found_command=true | |
cmd_and_args+=("$arg") | |
fi | |
done | |
if [[ ${#cmd_and_args[@]} -gt 0 ]]; then | |
local cmd="${cmd_and_args[0]}" | |
local args=("${cmd_and_args[@]:1}") | |
if declare -f "$cmd" >/dev/null 2>&1; then | |
local project_name="$(basename "$(pwd)")" | |
if [[ -f .devcontainer/devcontainer.json ]] || [[ -f devcontainer.json ]]; then | |
local env_exports="" | |
for env_var in "${env_vars[@]}"; do | |
env_exports+="export $(printf '%q' "$env_var"); " | |
done | |
local full_cmd="${env_exports}" | |
if [[ -x "bin/$cmd" ]]; then | |
full_cmd+="bin/$cmd" | |
else | |
full_cmd+="$cmd" | |
fi | |
for arg in "${args[@]}"; do | |
full_cmd+=" $(printf '%q' "$arg")" | |
done | |
devpod ssh "$project_name" --silent --workdir '/workspace' --command "bash -i -c $(printf '%q' "$full_cmd")" | |
else | |
if [[ -x "bin/$cmd" ]]; then | |
command env "${env_vars[@]}" "bin/$cmd" "${args[@]}" | |
else | |
command env "${env_vars[@]}" "$cmd" "${args[@]}" | |
fi | |
fi | |
else | |
command env "$@" | |
fi | |
else | |
command env "$@" | |
fi | |
} | |
# Hook to re-setup wrappers when changing directories | |
cd() { | |
builtin cd "$@" | |
setup_devcontainer_wrappers | |
} | |
pushd() { | |
builtin pushd "$@" | |
setup_devcontainer_wrappers | |
} | |
popd() { | |
builtin popd "$@" | |
setup_devcontainer_wrappers | |
} | |
# Initial setup | |
setup_devcontainer_wrappers |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment