Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
temporal: temporal server start-dev
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,25 @@ An oh-my-zsh plugin for switching Temporal contexts (`~/.temporal/config.yml`) s
- `temporalctx -`: switch to previous context
- `temporalctx -c`: print current context
- `temporalctx edit` (`-e`/`--edit`): open config in `$VISUAL`, then `$EDITOR`, else `vi`
- `temporalctx start`: start local Temporal dev server
- `temporalctx stop`: stop local Temporal dev server
- `tctx`: alias for `temporalctx`
- `temporal ...`: wrapped by plugin to automatically include current context flags

## Local Dev Server

`temporalctx start` and `temporalctx stop` support two modes:

- Overmind mode (recommended, optional): if `overmind` is installed, plugin uses it with a plugin `Procfile` and socket at `~/.temporal/overmind.sock`.
- PID mode (fallback): if `overmind` is not installed, plugin starts/stops `temporal server start-dev` directly and tracks `~/.temporal/temporal-dev-server.pid`.

Force PID mode even when Overmind is installed:

```bash
temporalctx start --no-overmind
temporalctx stop --no-overmind
```

## Helper function

`_temporal_flags` reads the active context and prints CLI flags:
Expand All @@ -29,10 +46,15 @@ To bypass wrapping for a command/session, set `TEMPORALCTX_DISABLE_WRAP=1`.
./install.sh
# or
./install.sh ~/my-custom-zsh/plugins
# or include opinionated tq/td/tl helpers in $ZSHC/temporal.zsh
./install.sh --full
```

Then add `temporalctx` to your `plugins=(...)` in `.zshrc`.

`--full` is optional and installs plugin-owned helper functions (`tq`, `td`, `tl`) by symlinking:
- `${ZSHC:-~/.config/zsh}/temporal.zsh` -> `<plugin-dir>/temporalctx.full.zsh`

## Config format

```yaml
Expand Down
51 changes: 45 additions & 6 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,37 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$SCRIPT_DIR"

if [[ $# -gt 1 ]]; then
echo "Usage: ./install.sh [target-directory]" >&2
exit 1
fi
usage() {
echo "Usage: ./install.sh [--full] [target-directory]" >&2
}

full_mode=0
user_target=""
while [[ $# -gt 0 ]]; do
case "$1" in
--full)
full_mode=1
shift
;;
-h|--help)
usage
exit 0
;;
--*)
echo "Unknown option: $1" >&2
usage
exit 1
;;
*)
if [[ -n "$user_target" ]]; then
usage
exit 1
fi
user_target="$1"
shift
;;
esac
done

if ! command -v temporal >/dev/null 2>&1; then
echo "temporal CLI is required but was not found in PATH." >&2
Expand All @@ -16,8 +43,7 @@ if ! command -v temporal >/dev/null 2>&1; then
fi

default_target_base="${ZSH_CUSTOM:-${ZSH:-$HOME/.oh-my-zsh}/custom}/plugins"
if [[ $# -eq 1 ]]; then
user_target="$1"
if [[ -n "$user_target" ]]; then
if [[ "$(basename "$user_target")" == "temporalctx" ]]; then
target_dir="$user_target"
else
Expand Down Expand Up @@ -73,6 +99,19 @@ else
echo "Config already exists at $config_file"
fi

if [[ "$full_mode" == "1" ]]; then
zshc_dir="${ZSHC:-$HOME/.config/zsh}"
helper_source="$target_dir/temporalctx.full.zsh"
helper_target="$zshc_dir/temporal.zsh"
mkdir -p "$zshc_dir"
if [[ ! -f "$helper_source" ]]; then
echo "Could not find opinionated helpers at $helper_source" >&2
exit 1
fi
ln -sfn "$helper_source" "$helper_target"
echo "Installed opinionated helpers at $helper_target"
fi

echo
echo "Installation complete."
echo "Add temporalctx to your Oh My Zsh plugins list, for example:"
Expand Down
27 changes: 27 additions & 0 deletions temporalctx.full.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Opinionated Temporal helper aliases/functions.
# Installed by `./install.sh --full`.

# Query a workflow's state.
# Usage: tq <workflow-id>
tq() {
local wf_id="${1:?Usage: tq <workflow-id>}"
temporal workflow query \
--workflow-id "$wf_id" \
--type getState
}

# Describe a workflow.
# Usage: td <workflow-id>
td() {
local wf_id="${1:?Usage: td <workflow-id>}"
temporal workflow describe \
--workflow-id "$wf_id"
}

# List recent workflows.
# Usage: tl [limit]
tl() {
local limit="${1:-10}"
temporal workflow list \
--limit "$limit"
}
118 changes: 118 additions & 0 deletions temporalctx.plugin.zsh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# temporalctx oh-my-zsh plugin
# Switch between Temporal contexts defined in ~/.temporal/config.yml.

typeset -g _TEMPORALCTX_PLUGIN_DIR="${${(%):-%x}:A:h}"

_temporalctx_config_file() {
if [[ -n "$TEMPORAL_CONFIG" ]]; then
print -r -- "$TEMPORAL_CONFIG"
Expand All @@ -15,6 +17,22 @@ _temporalctx_previous_file() {
print -r -- "${config_file:h}/.temporalctx-previous"
}

_temporalctx_pid_file() {
local config_file
config_file="$(_temporalctx_config_file)"
print -r -- "${config_file:h}/temporal-dev-server.pid"
}

_temporalctx_overmind_socket() {
local config_file
config_file="$(_temporalctx_config_file)"
print -r -- "${config_file:h}/overmind.sock"
}

_temporalctx_procfile() {
print -r -- "${_TEMPORALCTX_PLUGIN_DIR}/Procfile"
}

_temporalctx_default_config() {
cat <<'YAML'
current-context: local
Expand Down Expand Up @@ -213,6 +231,85 @@ _temporalctx_edit_config() {
${=editor_cmd} "$config_file"
}

_temporalctx_start_local_server() {
local no_overmind pid_file pid socket procfile
no_overmind="${1:-0}"

if [[ "$no_overmind" != "1" ]] && command -v overmind >/dev/null 2>&1; then
socket="$(_temporalctx_overmind_socket)"
procfile="$(_temporalctx_procfile)"
if [[ ! -f "$procfile" ]]; then
print -u2 -- "temporalctx: Procfile not found at $procfile"
return 1
fi
OVERMIND_SOCKET="$socket" overmind start -f "$procfile" -D >/dev/null 2>&1 || return 1
print -r -- "started local dev server via overmind"
return 0
fi

pid_file="$(_temporalctx_pid_file)"

if [[ -f "$pid_file" ]]; then
pid="$(<"$pid_file")"
if [[ -n "$pid" ]] && kill -0 "$pid" >/dev/null 2>&1; then
print -u2 -- "temporalctx: local dev server already running (pid $pid)"
return 1
fi
rm -f -- "$pid_file"
fi

if [[ -z "$(whence -p temporal)" ]]; then
print -u2 -- "temporalctx: temporal CLI not found in PATH"
return 127
fi

command temporal server start-dev >/dev/null 2>&1 &
pid="$!"
print -r -- "$pid" > "$pid_file"
print -r -- "started local dev server (pid $pid)"
}

_temporalctx_stop_local_server() {
local no_overmind pid_file pid i socket
no_overmind="${1:-0}"

if [[ "$no_overmind" != "1" ]] && command -v overmind >/dev/null 2>&1; then
socket="$(_temporalctx_overmind_socket)"
if [[ ! -S "$socket" && ! -e "$socket" ]]; then
print -u2 -- "temporalctx: local dev server not running"
return 1
fi
OVERMIND_SOCKET="$socket" overmind quit >/dev/null 2>&1 || return 1
print -r -- "stopped local dev server via overmind"
return 0
fi

pid_file="$(_temporalctx_pid_file)"

if [[ ! -f "$pid_file" ]]; then
print -u2 -- "temporalctx: local dev server not running"
return 1
fi

pid="$(<"$pid_file")"
if [[ -z "$pid" ]] || ! kill -0 "$pid" >/dev/null 2>&1; then
rm -f -- "$pid_file"
print -u2 -- "temporalctx: local dev server not running"
return 1
fi

kill "$pid" >/dev/null 2>&1 || true
for i in {1..20}; do
if ! kill -0 "$pid" >/dev/null 2>&1; then
break
fi
sleep 0.1
done

rm -f -- "$pid_file"
print -r -- "stopped local dev server (pid $pid)"
}

typeset -ga _temporalctx_flags

_temporalctx_build_flags() {
Expand Down Expand Up @@ -271,12 +368,31 @@ temporal() {
}

temporalctx() {
local no_overmind=0
_temporalctx_ensure_config

case "$1" in
"")
_temporalctx_pick_context
;;
start)
if [[ "$2" == "--no-overmind" ]]; then
no_overmind=1
elif [[ -n "$2" ]]; then
print -u2 -- "temporalctx: unknown option for start: $2"
return 1
fi
_temporalctx_start_local_server "$no_overmind"
;;
stop)
if [[ "$2" == "--no-overmind" ]]; then
no_overmind=1
elif [[ -n "$2" ]]; then
print -u2 -- "temporalctx: unknown option for stop: $2"
return 1
fi
_temporalctx_stop_local_server "$no_overmind"
;;
edit|-e|--edit)
_temporalctx_edit_config
;;
Expand Down Expand Up @@ -311,3 +427,5 @@ temporalctx() {
;;
esac
}

alias tctx='temporalctx'
12 changes: 12 additions & 0 deletions tests/install_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,16 @@ PATH="$tmp_root/bin:$PATH" HOME="$home2" "$REPO_ROOT/install.sh" "$custom_target
[[ -L "$custom_target/temporalctx" || -d "$custom_target/temporalctx/.git" ]] || fail "custom target should install to <dir>/temporalctx"
log "case passed: custom target"

# Test: --full installs opinionated helpers into ZSHC/temporal.zsh.
log "case: --full installs opinionated helpers in ZSHC"
home3="$tmp_root/home3"
zshc3="$tmp_root/zshc3"
plugins3="$tmp_root/plugins3"
mkdir -p "$home3" "$zshc3" "$plugins3"
PATH="$tmp_root/bin:$PATH" HOME="$home3" ZSHC="$zshc3" "$REPO_ROOT/install.sh" --full "$plugins3" >"$tmp_root/install3.out" 2>"$tmp_root/install3.err"
[[ -L "$zshc3/temporal.zsh" ]] || fail "--full should install temporal.zsh as a symlink in ZSHC"
link_target="$(readlink "$zshc3/temporal.zsh")"
assert_contains "$link_target" "temporalctx.full.zsh" "--full helper symlink should point to plugin helper file"
log "case passed: --full helpers"

echo "PASS: install.sh"
Loading