-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·260 lines (218 loc) · 7.43 KB
/
Copy pathinstall.sh
File metadata and controls
executable file
·260 lines (218 loc) · 7.43 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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#!/usr/bin/env bash
# Install zed-dbt-pane into a Zed workspace.
#
# Usage:
# bash install.sh [--update] [zed-workspace-dir]
#
# Args:
# --update / -u Overwrite existing files. Use this after `git pull`-ing
# the source repo to upgrade an existing install.
# <workspace> Default: current directory.
#
# IMPORTANT: pass your **Zed workspace root** — the directory you open in
# Zed, NOT the dbt project root. The script searches the workspace for the
# shallowest `dbt_project.yml` and points the runner at it.
#
# Layout after install:
# <workspace>/.zed/tasks.json — Zed task definition (cmd-enter binding)
# <workspace>/zed/run_query.py — runs `dbt show --inline` per cmd-enter
# <workspace>/zed/pane.py — Streamlit pane that polls the JSON
# <workspace>/zed/dbt_pane.sh — launches the Streamlit pane
# <workspace>/zed/README.md — in-project docs
# <workspace>/zed/.gitignore — gitignores zed/local/
# <workspace>/zed/local/ — runtime output (per-run JSON + log)
set -euo pipefail
update=false
target=""
while (( $# )); do
case "$1" in
-u|--update) update=true ;;
-h|--help)
sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//'
exit 0
;;
--) shift; target="${1:-}"; break ;;
-*) echo "unknown flag: $1" >&2; exit 2 ;;
*) target="$1" ;;
esac
shift
done
target="${target:-.}"
target="$(cd "$target" && pwd)"
src="$(cd "$(dirname "$0")" && pwd)"
# install_file <src-path> <dest-path>
#
# - If dest doesn't exist: copy.
# - If dest exists and is byte-identical to src: silent no-op.
# - If dest exists and differs:
# - With --update: overwrite.
# - Without: leave alone, print a hint.
install_file() {
local src_file="$1"
local dest_file="$2"
if [[ ! -f "$dest_file" ]]; then
cp "$src_file" "$dest_file"
echo " + ${dest_file#"$target/"}"
return
fi
if cmp -s "$src_file" "$dest_file"; then
return
fi
if $update; then
cp "$src_file" "$dest_file"
echo " ~ ${dest_file#"$target/"}"
else
echo " ! ${dest_file#"$target/"} differs from upstream — re-run with --update to refresh"
fi
}
# install_tasks_json
#
# Install / merge our task entry into <workspace>/.zed/tasks.json.
# Same spirit as the .gitignore handling below: append our entry only if
# it's not already there, never clobber the user's other tasks.
#
# - File missing -> write a fresh array containing just our task.
# - Our label already there + content matches -> silent no-op.
# - Our label there + content differs:
# --update -> replace it (preserving every other task in the array).
# default -> warn, leave alone.
# - Our label not there -> append our task to the existing array.
# - File has JSONC comments (json.loads fails) -> print snippet, leave
# alone (we won't silently strip user's comments).
install_tasks_json() {
DBT_SUBPATH="$dbt_subpath" \
TASKS_DEST="$target/.zed/tasks.json" \
TASKS_REL=".zed/tasks.json" \
TASKS_UPDATE="$($update && echo true || echo false)" \
python3 - <<'PYEOF'
import json
import os
import sys
dest = os.environ["TASKS_DEST"]
rel = os.environ["TASKS_REL"]
update = os.environ["TASKS_UPDATE"] == "true"
dbt_subpath = os.environ.get("DBT_SUBPATH", "")
LABEL = "dbt: run selection"
new_task = {
"label": LABEL,
"command": "bash",
"args": [
"-c",
"zed/run_query.py 2> >(tee zed/local/zed_query_last.log >&2)",
],
"cwd": "$ZED_WORKTREE_ROOT",
"use_new_terminal": False,
"reveal": "always",
"allow_concurrent_runs": False,
}
if dbt_subpath:
new_task["env"] = {"ZED_DBT_PROJECT_DIR": f"$ZED_WORKTREE_ROOT/{dbt_subpath}"}
def write(tasks):
with open(dest, "w") as f:
json.dump(tasks, f, indent=2)
f.write("\n")
if not os.path.exists(dest):
write([new_task])
print(f" + {rel}")
sys.exit(0)
with open(dest) as f:
content = f.read()
try:
tasks = json.loads(content)
except json.JSONDecodeError:
print(f" ! {rel} can't be auto-merged (likely contains JSONC comments).")
print( " Add this object to its top-level array manually:")
print()
for line in json.dumps(new_task, indent=2).splitlines():
print(f" {line}")
print()
sys.exit(0)
if not isinstance(tasks, list):
print(f" ! {rel} isn't a JSON array; leaving alone.")
sys.exit(0)
idx = next(
(i for i, t in enumerate(tasks) if isinstance(t, dict) and t.get("label") == LABEL),
-1,
)
if idx >= 0 and tasks[idx] == new_task:
sys.exit(0) # already there, identical — silent no-op
if idx >= 0:
if update:
tasks[idx] = new_task
write(tasks)
print(f" ~ {rel} (replaced '{LABEL}' task)")
else:
print(f" ! {rel} contains '{LABEL}' but content differs — re-run with --update to refresh")
sys.exit(0)
tasks.append(new_task)
write(tasks)
print(f" ~ {rel} (appended '{LABEL}' task; kept {len(tasks) - 1} existing)")
PYEOF
}
# Find the shallowest dbt_project.yml under $search_root.
# We walk by depth: check exactly depth 0, then 1, etc., and return the
# first match. Skips known-noisy directories so huge node_modules or
# dbt_packages trees don't slow us down.
shallowest_dbt_project_yml() {
local search_root="$1"
local depth
for depth in 0 1 2 3 4 5; do
local hit
hit=$(find "$search_root" -mindepth "$depth" -maxdepth "$depth" \
\( -name dbt_packages -o -name .venv -o -name .git \
-o -name node_modules -o -name target \) -prune \
-o -name dbt_project.yml -print \
2>/dev/null | head -1)
if [[ -n "$hit" ]]; then
echo "$hit"
return
fi
done
}
dbt_project_yml="$(shallowest_dbt_project_yml "$target")"
if [[ -z "$dbt_project_yml" ]]; then
echo "no dbt_project.yml found anywhere under $target" >&2
echo >&2
echo "Pass your Zed workspace root as the argument:" >&2
echo " bash $0 [--update] /path/to/your/zed/workspace" >&2
exit 1
fi
dbt_root="$(dirname "$dbt_project_yml")"
if [[ "$dbt_root" == "$target" ]]; then
dbt_subpath=""
else
dbt_subpath="${dbt_root#"$target"/}"
fi
mode="install"
$update && mode="update"
echo "Mode : $mode"
echo "Zed workspace : $target"
echo "dbt project : $dbt_root"
if [[ -n "$dbt_subpath" ]]; then
echo " (subdirectory '$dbt_subpath' — tasks.json sets ZED_DBT_PROJECT_DIR)"
fi
echo "Files:"
mkdir -p "$target/zed" "$target/.zed" "$target/zed/local"
install_file "$src/zed/run_query.py" "$target/zed/run_query.py"
install_file "$src/zed/pane.py" "$target/zed/pane.py"
install_file "$src/zed/dbt_pane.sh" "$target/zed/dbt_pane.sh"
install_file "$src/zed/README.md" "$target/zed/README.md"
install_file "$src/zed/.gitignore" "$target/zed/.gitignore"
install_tasks_json
chmod +x "$target/zed/run_query.py" "$target/zed/dbt_pane.sh" 2>/dev/null || true
cat <<EOF
zed-dbt-pane $mode complete at $target
Next steps:
1. Add the cmd-enter keybinding to ~/.config/zed/keymap.json. Append:
{
"context": "Editor && extension == sql",
"bindings": {
"cmd-enter": ["task::Spawn", { "task_name": "dbt: run selection" }]
}
}
Tip: ⌘⇧P → "open keymap file"
2. In a terminal, run the pane (powered by Streamlit)
bash $target/zed/dbt_pane.sh
3. Open a dbt model, select a subset (or don't), press cmd+enter.
See $target/zed/README.md for details.
EOF