-
Notifications
You must be signed in to change notification settings - Fork 53.5k
Expand file tree
/
Copy pathactivate
More file actions
executable file
·225 lines (209 loc) · 9.33 KB
/
Copy pathactivate
File metadata and controls
executable file
·225 lines (209 loc) · 9.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#!/usr/bin/env bash
# ============================================================================
# venv-style activation for the Hermes dev environment (pm-managed tools).
#
# source ./activate (bash / zsh, repo root)
# .\activate.ps1 (PowerShell)
#
# Emits the composed pm env (PATH + tool vars) into the CURRENT shell, with
# save/restore: `deactivate` undoes exactly what activation changed.
#
# `hermes` becomes a shell function for this checkout. It runs only while the
# shell is inside this worktree and refuses outside it, so a sibling worktree
# keeps its own command. A prompt prefix names the worktree.
#
# Sync through setup before selecting the environment. PM owns freshness;
# activation does not maintain a second dependency stamp. It trusts the
# recorded tool digest instead of re-hashing every entry. `hermes pm
# install` and `hermes update` keep that check.
# ============================================================================
_HERMES_REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Options: `--test-extras a,b` selects the test environment's runtime extras
# (default: [all]); `--` ends options.
# A bare `source ./activate` inherits the caller's positional parameters.
# Ignore unrelated words so scripts with their own arguments can source it.
_hermes_test_env="--test-environment"
_hermes_expect_extras=""
for _hermes_arg in "$@"; do
if [ -n "$_hermes_expect_extras" ]; then
_hermes_test_env="--test-environment=$_hermes_arg"
_hermes_expect_extras=""
continue
fi
case "$_hermes_arg" in
--) break ;;
--test-extras) _hermes_expect_extras=1 ;;
--test-extras=*) _hermes_test_env="--test-environment=${_hermes_arg#--test-extras=}" ;;
*) : ;;
esac
done
if [ -n "$_hermes_expect_extras" ]; then
printf '%s\n' 'activate: --test-extras needs a comma-separated list' >&2
unset _hermes_arg _hermes_test_env _hermes_expect_extras
return 2 2>/dev/null || exit 2
fi
# Setup runs in a child: failures must not exit or partially activate the
# caller's shell, and activation must not republish launchers or shell config.
if ! (
unset PYTHONHOME PYTHONPATH VIRTUAL_ENV
bash "$_HERMES_REPO/setup-hermes.sh" --runtime-only "$_hermes_test_env"
) >&2; then
printf '%s\n' 'activate: setup failed; shell environment unchanged' >&2
unset _hermes_arg _hermes_test_env _hermes_expect_extras
return 1 2>/dev/null || exit 1
fi
unset _hermes_arg _hermes_test_env _hermes_expect_extras
# Guard against double-sourcing: venv activate precedent (re-activating is a
# no-op re-save; we keep it idempotent by deactivating first). Key on the
# function, not the sentinel: child shells inherit __HERMES_ACTIVATED from an
# activated parent but never the function, and there is nothing to undo there.
if [ "$(type -t deactivate 2>/dev/null)" = "function" ]; then
deactivate
fi
# Bootstrap Python only emits the environment; it does not install anything.
_hermes_repo="$_HERMES_REPO"
_hermes_py=""
for _hermes_candidate in "$_hermes_repo/.venv/bin/python" "$_hermes_repo/.venv/Scripts/python.exe" \
"$_hermes_repo/venv/bin/python" "$_hermes_repo/venv/Scripts/python.exe"; do
[ -x "$_hermes_candidate" ] && { _hermes_py="$_hermes_candidate"; break; }
done
if [ -z "$_hermes_py" ]; then
for _hermes_store in "${HERMES_RUNTIME_DIR:-}" "$_hermes_repo/../tools" "${HERMES_HOME:-$HOME/.hermes}/tools"; do
[ -n "$_hermes_store" ] || continue
for _hermes_candidate in "$_hermes_store"/python-*/bin/python3 "$_hermes_store"/python-*/python.exe "$_hermes_store"/python-*/bin/python "$_hermes_store"/python-*/bin/python.exe; do
[ -x "$_hermes_candidate" ] && { _hermes_py="$_hermes_candidate"; break; }
done
[ -n "$_hermes_py" ] && break
done
fi
if [ -z "$_hermes_py" ]; then
echo "activate: no bootstrap Python found; run setup-hermes.sh" >&2
return 1 2>/dev/null || exit 1
fi
# --- emit export lines from `pm env` ---
_hermes_json="$(PYTHONHOME= PYTHONPATH="$_hermes_repo" "$_hermes_py" -m pm.environments)"
if [ -z "$_hermes_json" ] || [ "${_hermes_json#*{}" = "$_hermes_json" ]; then
echo "activate: could not read pm env (run ./setup-hermes.sh first)" >&2
unset _hermes_repo _hermes_os _hermes_arch _hermes_target _hermes_store _hermes_py _hermes_json
return 1 2>/dev/null || exit 1
fi
_hermes_keys="$(printf '%s' "$_hermes_json" | "$_hermes_py" -c 'import json,sys; print(" ".join(json.load(sys.stdin)))' \
| tr ' ' '\n' | grep -E '^[A-Za-z_][A-Za-z0-9_]*$' | tr '\n' ' ')"
[ -n "$_hermes_keys" ] || _hermes_keys="PATH"
# --- snapshot what we are about to change (deactivate restores this) ---
# __HERMES_ACTIVATED is part of the composed env below (it carries the
# installed-state path this environment was built from), so it is snapshotted
# and exported with every other key — no separate assignment here.
__HERMES_SAVED_PATH="$PATH"
for _hermes_k in $_hermes_keys; do
eval "__HERMES_SAVED_$_hermes_k=\"\${$_hermes_k+set}\""
eval "__HERMES_PRIOR_$_hermes_k=\${$_hermes_k-__HERMES_UNSET__}"
done
# Resolved before the export below replaces PATH.
_hermes_cygpath="$(command -v cygpath 2>/dev/null || :)"
eval "$(printf '%s' "$_hermes_json" | "$_hermes_py" -c '
import json, sys, shlex, re
for k, v in json.load(sys.stdin).items():
# Windows carries names like ProgramFiles(ARM) that are not valid
# bash identifiers — skip them (they pass through untouched).
if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", k):
continue
print("export %s=%s" % (k, shlex.quote(str(v))))')"
# The MSYS/Cygwin runtime hands a native Windows Python these variables in
# Windows form (`C:\a;C:\b`) and converts them back only for its own children.
# Exported verbatim, bash splits PATH on ':' and finds no commands.
if [ -n "$_hermes_cygpath" ]; then
PATH="$("$_hermes_cygpath" -u -p "$PATH")"
for _hermes_k in HOME TMPDIR TMP TEMP; do
if [ -n "${!_hermes_k-}" ]; then
printf -v "$_hermes_k" '%s' "$("$_hermes_cygpath" -u "${!_hermes_k}")"
fi
done
fi
# This checkout, not whichever `hermes` PATH finds. A function beats PATH, an
# alias, and a hashed command, including the MSIX execution alias.
__HERMES_WORKTREE="$_hermes_repo"
# The branch names the worktree. A checkout cannot share a branch with another.
__HERMES_WORKTREE_NAME="$(git -C "$_hermes_repo" rev-parse --abbrev-ref HEAD 2>/dev/null || :)"
if [ -z "$__HERMES_WORKTREE_NAME" ] || [ "$__HERMES_WORKTREE_NAME" = HEAD ]; then
__HERMES_WORKTREE_NAME="$(basename "$_hermes_repo")"
fi
__HERMES_SAVED_PS1="${PS1-__HERMES_UNSET__}"
__HERMES_SAVED_PROMPT_COMMAND="${PROMPT_COMMAND-__HERMES_UNSET__}"
_hermes_worktree_here() {
_hermes_top="$(git rev-parse --show-toplevel 2>/dev/null)" || return 1
[ "$(cd "$_hermes_top" && pwd -P)" = "$(cd "$__HERMES_WORKTREE" && pwd -P)" ]
}
hermes() {
_hermes_worktree_here || {
printf '%s\n' "hermes: $(pwd) is outside $__HERMES_WORKTREE; refusing (the installed command is hidden while this checkout is active)" >&2
return 1
}
(
cd "$__HERMES_WORKTREE" || exit 1
if [ -n "${PYTHON-}" ]; then
exec "$PYTHON" hermes "$@"
elif [ -x .venv/Scripts/python.exe ]; then
exec .venv/Scripts/python.exe hermes "$@"
elif [ -x .venv/bin/python ]; then
exec .venv/bin/python hermes "$@"
elif [ -x venv/Scripts/python.exe ]; then
exec venv/Scripts/python.exe hermes "$@"
elif [ -x venv/bin/python ]; then
exec venv/bin/python hermes "$@"
else
exec python hermes "$@"
fi
)
}
_hermes_prompt() {
if [ "$__HERMES_SAVED_PS1" = "__HERMES_UNSET__" ]; then
_hermes_base_ps1=""
else
_hermes_base_ps1="$__HERMES_SAVED_PS1"
fi
if _hermes_worktree_here; then
PS1="($__HERMES_WORKTREE_NAME) $_hermes_base_ps1"
else
PS1="$_hermes_base_ps1"
fi
}
if [ -n "${PROMPT_COMMAND-}" ]; then
PROMPT_COMMAND="_hermes_prompt; ${PROMPT_COMMAND}"
else
PROMPT_COMMAND=_hermes_prompt
fi
_hermes_prompt
deactivate() {
export PATH="$__HERMES_SAVED_PATH"
for _hermes_k in $__HERMES_KEY_LIST; do
eval "_hermes_was=\"\$__HERMES_SAVED_$_hermes_k\""
eval "_hermes_old=\"\$__HERMES_PRIOR_$_hermes_k\""
if [ "$_hermes_was" != "set" ]; then
unset "$_hermes_k"
else
export "$_hermes_k=$_hermes_old"
fi
unset "__HERMES_SAVED_$_hermes_k" "__HERMES_PRIOR_$_hermes_k"
done
if [ "$__HERMES_SAVED_PS1" = "__HERMES_UNSET__" ]; then
unset PS1
else
PS1="$__HERMES_SAVED_PS1"
fi
if [ "$__HERMES_SAVED_PROMPT_COMMAND" = "__HERMES_UNSET__" ]; then
unset PROMPT_COMMAND
else
PROMPT_COMMAND="$__HERMES_SAVED_PROMPT_COMMAND"
fi
unset __HERMES_SAVED_PATH __HERMES_KEY_LIST __HERMES_ACTIVATED \
__HERMES_WORKTREE __HERMES_WORKTREE_NAME \
__HERMES_SAVED_PS1 __HERMES_SAVED_PROMPT_COMMAND
unset -f deactivate hermes _hermes_worktree_here _hermes_prompt
unset _hermes_k _hermes_was _hermes_old _hermes_top
}
# remember the key list for deactivate (kept out of the loop vars above)
__HERMES_KEY_LIST="$_hermes_keys"
unset _hermes_repo _hermes_os _hermes_arch _hermes_target _hermes_store \
_hermes_py _hermes_json _hermes_keys _hermes_k _hermes_entry _hermes_venvpy \
_hermes_winarch _hermes_top _hermes_cygpath