fix(databricks-devtools): populate empty referenced directories and fix shellcheck warnings - #477
Conversation
…ix shellcheck warnings Add all missing referenced files promised by the Databricks skills documentation. databricks-cli: - Add references/authentication.md (PAT, OAuth U2M, OAuth M2M methods) - Add references/commands.md (complete CLI command reference) - Add examples/databrickscfg (sample configuration) - Add scripts/test-auth.sh (authentication test script) databricks-workspace: - Add references/paths.md (workspace path conventions) - Add references/formats.md (notebook format details) - Add references/permissions.md (access control guide) - Add examples/quick-start.sh (workspace operations demo) - Add examples/run-simple.sh (quick code execution tool) - Fix shellcheck warnings in quick-start.sh and run-simple.sh databricks-jobs: - Add references/workflows.md (advanced workflow patterns) - Add references/troubleshooting.md (detailed troubleshooting) - Add examples/simple-job.json (basic job config) - Add examples/workflow.json (multi-task ETL workflow) - Add examples/scheduled-job.json (scheduled job with cron) - Add scripts/run-and-wait.sh (execute and wait for completion) - Add scripts/list-failed-runs.sh (list failed job runs) - Fix shellcheck warnings in run-and-wait.sh and list-failed-runs.sh All scripts are executable and pass shellcheck validation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR adds comprehensive documentation, configuration examples, and utility scripts for Databricks development tools. Changes span authentication guides, CLI command references, job configuration examples, troubleshooting guides, and bash scripts for authentication testing, job management, and workspace operations across databricks-cli, databricks-jobs, and databricks-workspace skills. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~28 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugins/databricks-devtools/skills/databricks-workspace/SKILL.md (1)
252-258:⚠️ Potential issue | 🟡 MinorIncorrect CLI subcommand names:
export_dirandimport_dirshould use hyphens.The Databricks CLI uses hyphenated subcommands. These will fail if copy-pasted.
-databricks --profile source workspace export_dir /Users/user@example.com ./backup +databricks --profile source workspace export-dir /Users/user@example.com ./backup -databricks --profile target workspace import_dir ./backup /Users/user@example.com +databricks --profile target workspace import-dir ./backup /Users/user@example.com
🤖 Fix all issues with AI agents
In `@plugins/databricks-devtools/skills/databricks-cli/scripts/test-auth.sh`:
- Around line 50-56: Add strict shell flags at the top of test-auth.sh (set -euo
pipefail) and change all failure/diagnostic outputs in the test blocks to write
to stderr: redirect print_result failure invocations and any subsequent echo
lines using >&2 (e.g., print_result "Databricks CLI installed" "FAIL"
"databricks command not found" >&2 and echo "Install Databricks CLI:" >&2).
Apply the same stderr redirection pattern to the other failure blocks (Tests
2–4) so all error messages are emitted to stderr.
In
`@plugins/databricks-devtools/skills/databricks-jobs/scripts/list-failed-runs.sh`:
- Around line 393-395: The script calls log_debug after computing
cutoff=$(get_cutoff_timestamp) but log_debug is not defined (with set -euo
pipefail this will abort); either add a log_debug function alongside the other
logging helpers (matching their signature and behavior) or change the call to
use an existing defined logger (e.g., log_info/log_verbose) and ensure that
logger is available in the script; update references around get_cutoff_timestamp
to use the chosen, defined logging function so the script no longer tries to
invoke an undefined command.
- Around line 296-302: The use of `date -r` to convert epoch timestamps in the
timestamp conversion block (variables formatted_date and formatted_duration,
where start_time is checked) is macOS/BSD-specific and will fail on GNU/Linux;
update the script to detect which date implementation is available (e.g., check
if `date -r` works or if `date -d` is supported) and use the appropriate
invocation (`date -r <epoch>` for BSD or `date -d @<epoch>` for GNU) when
formatting `start_time` (and the similar conversion later around
formatted_duration), falling back to "Unknown" on failure so existing error
handling remains. Ensure you modify the same timestamp conversion logic that
sets formatted_date and formatted_duration.
- Around line 360-367: The current CSV escaping replacing commas with
backslashes is invalid; update the logic that prepares job_name, run_name,
state_message (and any other printed fields like job_id or formatted_date) to
follow RFC 4180: write a helper (e.g., csv_escape_field) that, for a given
string, doubles any internal double-quotes and wraps the entire field in
double-quotes if it contains a comma, double-quote, or newline, then call that
helper for job_name, run_name, state_message (and other fields as needed) before
the printf so the printed CSV uses proper quoting.
- Around line 163-199: The loop in list_failed_runs is unsafe and inefficient:
stop interpolating $job_id and $job_name directly into the jq program and stop
invoking jq per-run; instead rewrite the logic to produce the failed-runs array
with a single jq invocation that uses --arg (e.g. --arg job_id "$job_id" --arg
job_name "$job_name") to inject those values safely and filters .runs[] |
select(.state.result_state=="FAILED") | {job_id: $job_id, job_name: $job_name,
run_id: .run_id, run_name: (.run_name // "<unnamed>"), start_time: (.start_time
// 0), duration: ((.run_duration // 0) / 1000), state: .state.life_cycle_state,
result_state: .state.result_state, state_message: (.state.state_message // ""),
triggering_event: (.trigger // "manual")} and wrap as an array; apply the same
pattern to get_all_failed_runs (replace per-iteration jq and manual array_string
construction with a single jq filter and --arg usage).
In `@plugins/databricks-devtools/skills/databricks-jobs/scripts/run-and-wait.sh`:
- Around line 42-56: The log functions send all output to stdout; change
log_error and log_warning to write to stderr (redirect their echo calls to >&2
or use >&2 with printf) and leave log_info/log_success writing to stdout; also
ensure the script starts with set -euo pipefail per policy (add it at the top of
this script) so it fails safely—look for and update the functions named
log_error and log_warning and add the set -euo pipefail header near the top of
run-and-wait.sh.
- Around line 284-295: The script currently uses set -euo pipefail which causes
the shell to exit immediately if poll_status returns non-zero, so main() never
reaches "local exit_code=$?" and display_output isn't called; modify main() to
temporarily disable errexit before invoking poll_status (for example use "set
+e" or call "poll_status || true"), capture its exit code into exit_code, then
restore errexit (e.g., "set -e") and proceed to call display_output when
exit_code != 0; reference the main function and the poll_status and
display_output symbols when making the change.
- Around line 112-126: The validation for --timeout and --poll-interval
currently allows "0" which later causes division-by-zero and infinite-loop
issues; update the checks in run-and-wait.sh for TIMEOUT and POLL_INTERVAL to
require positive integers greater than zero (e.g. use a regex like ^[1-9][0-9]*$
or parse and test numeric > 0) and keep the existing error messages (or slightly
adjust to "Must be a positive integer > 0") and assignments to TIMEOUT and
POLL_INTERVAL so invalid "0" is rejected before shift and exit 3 is called.
In
`@plugins/databricks-devtools/skills/databricks-workspace/examples/databricks-tools.sh`:
- Around line 29-46: In run(), avoid the hardcoded /tmp/nb.py by creating a
unique temp notebook file (use mktemp or similar) and reference that path when
creating / importing the notebook; after databricks jobs submit, do not read
.tasks[0].state.result_state immediately—instead implement a polling/wait loop
(similar to run-simple.sh) that polls the run status until completion and then
reads the final result_state and execution_duration from the completed run; make
workspace delete resilient by appending || true to the databricks workspace
delete call (referencing workspace delete "$temp") and make the local cleanup
robust by using rm -f on the temp file path; ensure errors from databricks
commands are handled or propagated appropriately so failures don't produce
misleading null results.
- Around line 15-18: The script shadows the shell BUILTIN variable USER and
masks the databricks command exit code; rename USER to DB_USER and capture the
databricks output in a separate command so the exit status isn't masked.
Specifically, replace the USER variable reference with DB_USER, run the
databricks --profile "$PROFILE" current-user me --output json command on its own
line (capture its output into a temporary variable or check its exit code), then
pipe that output to jq -r '.userName' to assign DB_USER; update all subsequent
$USER references to $DB_USER and keep PROFILE as-is.
In
`@plugins/databricks-devtools/skills/databricks-workspace/examples/quick-start.sh`:
- Around line 160-163: The jq filter string in the pipeline that starts with the
databricks repos list command is missing a closing double quote, causing a jq
parse error; fix it by adding the missing closing double quote at the end of the
jq filter passed to jq -r so the expression inside the single quotes becomes a
properly balanced string (the pipeline is the one starting with databricks
--profile "$PROFILE" repos list --output json | jq -r and producing '.[] |
"\(.path | split("/") | .[-1]) - \(.url)').
In
`@plugins/databricks-devtools/skills/databricks-workspace/examples/run-simple.sh`:
- Around line 194-217: The function run_code always returns 0 even when the
Databricks job fails; update its control flow to propagate failure to callers by
returning a non-zero exit code: introduce an exit_code variable (default 0) in
run_code, set exit_code=1 when final_state != "TERMINATED" or when result_state
matches FAILED|TIMEDOUT|CANCELED (the branches that call error), and replace the
unconditional return 0 with return $exit_code so callers and set -e detect
failures; reference variables final_state and result_state and the run_id
handling when locating the change.
🟡 Minor comments (10)
plugins/databricks-devtools/skills/databricks-cli/references/commands.md-387-396 (1)
387-396:⚠️ Potential issue | 🟡 MinorWait-for-cluster snippet lacks timeout and has unquoted variables.
Users will likely copy-paste this pattern. The loop runs forever if the cluster fails to start (e.g., enters
TERMINATEDorERRORstate), and$CLUSTER_IDshould be quoted.Suggested improvement
-databricks clusters start --cluster-id $CLUSTER_ID +databricks clusters start --cluster-id "$CLUSTER_ID" while true; do - STATE=$(databricks clusters get --cluster-id $CLUSTER_ID --output json | jq -r '.state') + STATE=$(databricks clusters get --cluster-id "$CLUSTER_ID" --output json | jq -r '.state') if [ "$STATE" = "RUNNING" ]; then break fi + if [ "$STATE" = "TERMINATED" ] || [ "$STATE" = "ERROR" ]; then + echo "Cluster entered $STATE state" + exit 1 + fi sleep 10 doneplugins/databricks-devtools/skills/databricks-cli/references/authentication.md-110-145 (1)
110-145:⚠️ Potential issue | 🟡 MinorAdd blank lines before fenced code blocks.
The code blocks under "Solution:" (lines 113, 124, 139) are missing a preceding blank line, which violates MD031 (blanks-around-fences) and may cause rendering issues in some Markdown parsers.
Proposed fix (example for lines 110-117)
**Cause:** Token expired or invalid **Solution:** + ```bash # Verify token format (should start with dapi)Apply the same pattern at lines 123–124 and 138–139.
plugins/databricks-devtools/skills/databricks-cli/scripts/test-auth.sh-144-155 (1)
144-155:⚠️ Potential issue | 🟡 MinorSummary unconditionally reports success, even when tests 5–7 fail.
If Test 5 (
current-user me) returns FAIL, the script still prints "All critical tests passed!" Consider tracking a warning/failure counter and adjusting the summary accordingly.Suggested approach
+WARNINGS=0 +FAILURES=0 + # Colors for outputThen increment in the respective branches, and at the summary:
echo "=====================================" echo "Authentication Test Summary" echo "=====================================" -echo -e "${GREEN}All critical tests passed!${NC}" +if [ "$FAILURES" -gt 0 ]; then + echo -e "${RED}Some tests failed ($FAILURES failure(s), $WARNINGS warning(s))${NC}" +elif [ "$WARNINGS" -gt 0 ]; then + echo -e "${YELLOW}Critical tests passed with $WARNINGS warning(s)${NC}" +else + echo -e "${GREEN}All tests passed!${NC}" +fiplugins/databricks-devtools/skills/databricks-workspace/examples/quick-start.sh-226-231 (1)
226-231:⚠️ Potential issue | 🟡 MinorContradictory message: files are not retained since the EXIT trap runs cleanup.
Line 229 says "Files retained in $DEMO_DIR for inspection", but the
trap demo_cleanup EXITon line 216 will delete$DEMO_DIRimmediately whenmainreturns. Either remove the trap to allow inspection, or remove the misleading message.plugins/databricks-devtools/skills/databricks-jobs/examples/workflow.json-121-136 (1)
121-136:⚠️ Potential issue | 🟡 Minor
cleanuptask dependency logic may not work as intended.The
cleanuptask depends on bothload_gold(SUCCESS) andhandle_validation_failure(SUCCESS). In Databricks, when a task has multipledepends_onentries, all conditions must be met (AND logic). Sinceload_goldandhandle_validation_failureare on mutually exclusive branches (one runs on validation success, the other on failure),cleanupwill never execute — one dependency will always be in a non-SUCCESS state.To run cleanup regardless of which branch executed, consider using the
run_iftask configuration or restructuring so cleanup depends on each branch outcome independently.plugins/databricks-devtools/skills/databricks-jobs/examples/workflow.json-12-24 (1)
12-24:⚠️ Potential issue | 🟡 MinorRemove
num_workers— it is mutually exclusive withautoscalein Databricks cluster configs.Databricks API requires specifying either
num_workers(for fixed-size clusters) orautoscale(for autoscaling clusters), not both. Since this example uses autoscaling, remove thenum_workersfield.Proposed fix
"new_cluster": { "cluster_name": "ETL Job Cluster", "spark_version": "13.3.x-scala2.12", "node_type_id": "i3.xlarge", - "num_workers": 4, "autoscale": { "min_workers": 2, "max_workers": 8 },plugins/databricks-devtools/skills/databricks-jobs/scripts/list-failed-runs.sh-370-385 (1)
370-385:⚠️ Potential issue | 🟡 Minor
--include-outputdoesn't actually fetch error output.The comment on line 375 says "Add error output to each run" and the
get_error_outputfunction exists (line 254), butformat_jsonnever calls it. The flag only adds formatted timestamps — not error output.plugins/databricks-devtools/skills/databricks-workspace/examples/run-simple.sh-109-113 (1)
109-113:⚠️ Potential issue | 🟡 MinorOrphaned
mkdircreates a useless directory with a mismatched timestamp.Line 113 calls
mkdir -p "/tmp/nb_$(date +%s)"which creates a directory with a newdate +%svalue (potentially different from the one used fortemp_fileon line 110). This directory is never used and never cleaned up. It appears to be a leftover or copy-paste error.Additionally, the two
date +%scalls on lines 109–110 can produce different timestamps if they straddle a second boundary, causingtemp_pathandtemp_fileto be mismatched (cosmetic, but unnecessarily confusing).Proposed fix
+ local ts + ts=$(date +%s) + # Create temporary notebook path - temp_path="/Users/$user/.databricks-cli-temp/run_$(date +%s)" - temp_file="/tmp/nb_$(date +%s).py" - - # Create temporary notebook file - mkdir -p "/tmp/nb_$(date +%s)" 2>/dev/null || true + temp_path="/Users/$user/.databricks-cli-temp/run_${ts}" + temp_file="/tmp/nb_${ts}.py" + cat > "$temp_file" << EOFplugins/databricks-devtools/skills/databricks-workspace/examples/run-simple.sh-114-118 (1)
114-118:⚠️ Potential issue | 🟡 MinorHeredoc delimiter
EOFcan conflict with user-supplied code.If
$codecontains the literal stringEOFon a line by itself, the heredoc will terminate prematurely. Using a more unique or quoted delimiter mitigates this.Proposed fix
- cat > "$temp_file" << EOF + cat > "$temp_file" << 'NOTEBOOK_EOF' # Databricks notebook source # Command 1 -$code -EOF +NOTEBOOK_EOF + echo "$code" >> "$temp_file"plugins/databricks-devtools/skills/databricks-workspace/examples/databricks-tools.sh-30-34 (1)
30-34:⚠️ Potential issue | 🟡 MinorSC2155: Declare and assign separately to avoid masking return values.
Static analysis flagged line 34. If
get_clusterfails, the exit code is masked.Proposed fix
local cluster + cluster=$(get_cluster) - cluster=$(get_cluster) - local temp="/Users/$USER/.temp/run_$(date +%s)" + local temp + temp="/Users/$DB_USER/.temp/run_$(date +%s)"
🧹 Nitpick comments (9)
plugins/databricks-devtools/skills/databricks-workspace/references/paths.md (1)
7-9: Add language specifiers to fenced code blocks.The path-example code blocks at lines 7, 20, 31, 42, and 53 lack a language identifier (MD040). Adding
```textwould satisfy the linter and improve rendering consistency.plugins/databricks-devtools/skills/databricks-workspace/references/permissions.md (1)
41-45: Add language specifier to fenced code blocks.Code blocks at lines 41 and 178 lack a language identifier (MD040). Use
```textfor plain-text examples.plugins/databricks-devtools/skills/databricks-cli/scripts/test-auth.sh (1)
78-78: Profile name is interpolated directly into a regex pattern.If
DATABRICKS_PROFILEcontains regex metacharacters (e.g.,.,+), thegrepmatch could produce false positives. Consider using fixed-string matching with a more explicit pattern.Suggested fix
-if grep -q "^\[$PROFILE\]" "$HOME/.databrickscfg" 2>/dev/null; then +if grep -qF "[$PROFILE]" "$HOME/.databrickscfg" 2>/dev/null; thenThis trades the
^anchor for safety against metacharacter injection. Profile names in.databrickscfgare typically on their own line, sogrep -Fis sufficient.plugins/databricks-devtools/skills/databricks-workspace/references/formats.md (1)
109-115: Fenced code block missing language specifier.Static analysis flags this block (MD040). Add a language identifier for consistency.
Proposed fix
-``` +```text # Command N [Cell content here] # Command N+1 [More content] ```plugins/databricks-devtools/skills/databricks-workspace/examples/quick-start.sh (1)
191-205:get_user()call indemo_cleanupis unused and fragile.Line 194 calls
get_user()and assigns toUSER, butUSERis never referenced in this function. Underset -e, if the Databricks CLI call insideget_user()fails during cleanup (e.g., network issue), the trap handler itself would fail, potentially skipping local file cleanup on line 203.Proposed fix — remove unused call
demo_cleanup() { log "Cleaning up demo resources" - USER=$(get_user) local demo_base demo_base=$(cat "$DEMO_DIR/base_path.txt" 2>/dev/null || true)plugins/databricks-devtools/skills/databricks-workspace/examples/run-simple.sh (1)
56-74:validate_clustercallsclusters gettwice — redundant API call.The first call (line 60) checks accessibility, then the second call (line 66) fetches state. You can combine these into a single call.
Proposed fix
validate_cluster() { local cluster_id="$1" + local cluster_info - databricks --profile "$PROFILE" clusters get "$cluster_id" --output json >/dev/null 2>&1 || { + cluster_info=$(databricks --profile "$PROFILE" clusters get "$cluster_id" --output json 2>/dev/null) || { error "Cluster $cluster_id not found or not accessible" return 1 } local state - state=$(databricks --profile "$PROFILE" clusters get "$cluster_id" --output json | \ - jq -r '.state') + state=$(echo "$cluster_info" | jq -r '.state') if [ "$state" != "RUNNING" ]; then error "Cluster $cluster_id is not running (state: $state)" return 1 fi return 0 }plugins/databricks-devtools/skills/databricks-workspace/examples/databricks-tools.sh (1)
49-54:uploaddoesn't validate that$1is provided.Under
set -euo pipefailwithnounset, callinguploadwithout arguments will abort with an unhelpful "unbound variable" error. A guard with a descriptive message would improve usability.plugins/databricks-devtools/skills/databricks-workspace/SKILL.md (1)
52-65: Add a language specifier to the fenced code block.Static analysis (MD040) flags this block as missing a language. Use
```textfor the directory tree diagram.plugins/databricks-devtools/skills/databricks-jobs/scripts/list-failed-runs.sh (1)
132-140: Timestamp arithmetic via string concatenation is fragile.Line 138 uses
$(date +%s)000to get milliseconds by appending000as a string. This works but is brittle and non-obvious. Consider:- current_ms=$(date +%s)000 + current_ms=$(( $(date +%s) * 1000 ))
| else | ||
| print_result "Databricks CLI installed" "FAIL" "databricks command not found" | ||
| echo "" | ||
| echo "Install Databricks CLI:" | ||
| echo " brew install databricks # macOS" | ||
| echo " pip install databricks-cli # Python" | ||
| exit 1 |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Error messages should be written to stderr.
The coding guideline requires error messages to go to stderr. Failure output (e.g., lines 51, 65–71, 100–108) is currently written to stdout. Redirect error/diagnostic messages with >&2.
Example fix for Test 1 failure
print_result "Databricks CLI installed" "FAIL" "databricks command not found"
- echo ""
- echo "Install Databricks CLI:"
- echo " brew install databricks # macOS"
- echo " pip install databricks-cli # Python"
+ echo "" >&2
+ echo "Install Databricks CLI:" >&2
+ echo " brew install databricks # macOS" >&2
+ echo " pip install databricks-cli # Python" >&2
exit 1Apply the same pattern to the other failure blocks (Tests 2–4).
As per coding guidelines, **/*.sh: "Hook scripts must include set -euo pipefail at the start and use jq for JSON parsing with stderr for error messages".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| else | |
| print_result "Databricks CLI installed" "FAIL" "databricks command not found" | |
| echo "" | |
| echo "Install Databricks CLI:" | |
| echo " brew install databricks # macOS" | |
| echo " pip install databricks-cli # Python" | |
| exit 1 | |
| else | |
| print_result "Databricks CLI installed" "FAIL" "databricks command not found" | |
| echo "" >&2 | |
| echo "Install Databricks CLI:" >&2 | |
| echo " brew install databricks # macOS" >&2 | |
| echo " pip install databricks-cli # Python" >&2 | |
| exit 1 |
🤖 Prompt for AI Agents
In `@plugins/databricks-devtools/skills/databricks-cli/scripts/test-auth.sh`
around lines 50 - 56, Add strict shell flags at the top of test-auth.sh (set
-euo pipefail) and change all failure/diagnostic outputs in the test blocks to
write to stderr: redirect print_result failure invocations and any subsequent
echo lines using >&2 (e.g., print_result "Databricks CLI installed" "FAIL"
"databricks command not found" >&2 and echo "Install Databricks CLI:" >&2).
Apply the same stderr redirection pattern to the other failure blocks (Tests
2–4) so all error messages are emitted to stderr.
| for ((i=0; i<run_count; i++)); do | ||
| local result_state | ||
| result_state=$(echo "$runs" | jq -r ".runs[$i].state.result_state // \"\"") | ||
|
|
||
| if [[ "$result_state" == "FAILED" ]]; then | ||
| local run_info | ||
| run_info=$(echo "$runs" | jq ".runs[$i] | { | ||
| job_id: \"$job_id\", | ||
| job_name: \"$job_name\", | ||
| run_id: .run_id, | ||
| run_name: .run_name // \"<unnamed>\", | ||
| start_time: (.start_time // 0), | ||
| duration: (.run_duration // 0) / 1000, | ||
| state: .state.life_cycle_state, | ||
| result_state: .state.result_state, | ||
| state_message: .state.state_message // \"\", | ||
| triggering_event: (.trigger // \"manual\") | ||
| }") | ||
| failed_runs+=("$run_info") | ||
| fi | ||
| done | ||
|
|
||
| # Output as JSON array | ||
| local array_string="[" | ||
| local first=true | ||
| for run in "${failed_runs[@]}"; do | ||
| if [[ "$first" == "true" ]]; then | ||
| first=false | ||
| else | ||
| array_string+="," | ||
| fi | ||
| array_string+="$run" | ||
| done | ||
| array_string+="]" | ||
|
|
||
| echo "$array_string" | ||
| } |
There was a problem hiding this comment.
Shell-loop-over-jq anti-pattern and unsafe string interpolation into jq.
Two issues here:
-
Injection risk (lines 169–180):
$job_idand$job_nameare interpolated directly into the jq expression via bash double-quotes. Ifjob_namecontains a backslash, double-quote, or other special character, the jq filter will break or produce malformed JSON. Use jq's--argfor safe injection. -
Performance: Calling
jqonce per loop iteration (lines 165, 169) on the same JSON blob is O(n) calls where a singlejqfilter could do all the work.
Proposed fix — replace the loop with a single jq call
get_failed_runs_for_job() {
local job_id="$1"
local job_name="$2"
- local runs
- local failed_runs=()
+ local runs
if ! runs=$(databricks jobs list-runs --job-id "$job_id" --limit "$LIMIT" --output json 2>/dev/null); then
log_error "Failed to get runs for job $job_id"
return 1
fi
- # Filter for failed runs
- local run_count
- run_count=$(echo "$runs" | jq '.runs | length')
-
- for ((i=0; i<run_count; i++)); do
- local result_state
- result_state=$(echo "$runs" | jq -r ".runs[$i].state.result_state // \"\"")
-
- if [[ "$result_state" == "FAILED" ]]; then
- local run_info
- run_info=$(echo "$runs" | jq ".runs[$i] | {
- job_id: \"$job_id\",
- job_name: \"$job_name\",
- ...
- }")
- failed_runs+=("$run_info")
- fi
- done
-
- # Output as JSON array
- ...
-
- echo "$array_string"
+ echo "$runs" | jq --arg jid "$job_id" --arg jname "$job_name" '
+ [.runs[] | select(.state.result_state == "FAILED") | {
+ job_id: $jid,
+ job_name: $jname,
+ run_id: .run_id,
+ run_name: (.run_name // "<unnamed>"),
+ start_time: (.start_time // 0),
+ duration: ((.run_duration // 0) / 1000),
+ state: .state.life_cycle_state,
+ result_state: .state.result_state,
+ state_message: (.state.state_message // ""),
+ triggering_event: (.trigger // "manual")
+ }]
+ '
}This same pattern applies to get_all_failed_runs (lines 216–251) and the manual JSON array construction there.
🤖 Prompt for AI Agents
In
`@plugins/databricks-devtools/skills/databricks-jobs/scripts/list-failed-runs.sh`
around lines 163 - 199, The loop in list_failed_runs is unsafe and inefficient:
stop interpolating $job_id and $job_name directly into the jq program and stop
invoking jq per-run; instead rewrite the logic to produce the failed-runs array
with a single jq invocation that uses --arg (e.g. --arg job_id "$job_id" --arg
job_name "$job_name") to inject those values safely and filters .runs[] |
select(.state.result_state=="FAILED") | {job_id: $job_id, job_name: $job_name,
run_id: .run_id, run_name: (.run_name // "<unnamed>"), start_time: (.start_time
// 0), duration: ((.run_duration // 0) / 1000), state: .state.life_cycle_state,
result_state: .state.result_state, state_message: (.state.state_message // ""),
triggering_event: (.trigger // "manual")} and wrap as an array; apply the same
pattern to get_all_failed_runs (replace per-iteration jq and manual array_string
construction with a single jq filter and --arg usage).
| # Convert timestamps | ||
| local formatted_date formatted_duration | ||
| if [[ "$start_time" != "0" ]]; then | ||
| formatted_date=$(date -r "$((start_time / 1000))" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "Unknown") | ||
| else | ||
| formatted_date="Unknown" | ||
| fi |
There was a problem hiding this comment.
date -r is macOS-specific — will fail on Linux.
date -r <epoch> is a BSD/macOS extension. On GNU/Linux, the equivalent is date -d @<epoch>. This also affects line 349.
Proposed portable alternative
- formatted_date=$(date -r "$((start_time / 1000))" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "Unknown")
+ formatted_date=$(date -d "@$((start_time / 1000))" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || \
+ date -r "$((start_time / 1000))" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || \
+ echo "Unknown")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Convert timestamps | |
| local formatted_date formatted_duration | |
| if [[ "$start_time" != "0" ]]; then | |
| formatted_date=$(date -r "$((start_time / 1000))" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "Unknown") | |
| else | |
| formatted_date="Unknown" | |
| fi | |
| # Convert timestamps | |
| local formatted_date formatted_duration | |
| if [[ "$start_time" != "0" ]]; then | |
| formatted_date=$(date -d "@$((start_time / 1000))" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || \ | |
| date -r "$((start_time / 1000))" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || \ | |
| echo "Unknown") | |
| else | |
| formatted_date="Unknown" | |
| fi |
🤖 Prompt for AI Agents
In
`@plugins/databricks-devtools/skills/databricks-jobs/scripts/list-failed-runs.sh`
around lines 296 - 302, The use of `date -r` to convert epoch timestamps in the
timestamp conversion block (variables formatted_date and formatted_duration,
where start_time is checked) is macOS/BSD-specific and will fail on GNU/Linux;
update the script to detect which date implementation is available (e.g., check
if `date -r` works or if `date -d` is supported) and use the appropriate
invocation (`date -r <epoch>` for BSD or `date -d @<epoch>` for GNU) when
formatting `start_time` (and the similar conversion later around
formatted_duration), falling back to "Unknown" on failure so existing error
handling remains. Ensure you modify the same timestamp conversion logic that
sets formatted_date and formatted_duration.
| # Escape CSV fields | ||
| job_name="${job_name//,/\\,}" | ||
| run_name="${run_name//,/\\,}" | ||
| state_message="${state_message//,/\\,}" | ||
|
|
||
| printf "%s,%s,%s,%s,%s,%s,%s\n" \ | ||
| "$job_id" "$job_name" "$run_name" "$formatted_date" "$formatted_duration" "$triggering" "$state_message" | ||
| done |
There was a problem hiding this comment.
CSV escaping is incorrect — fields with commas, quotes, or newlines need RFC 4180 quoting.
Replacing , with \, (lines 361–363) is not valid CSV. Per RFC 4180, fields containing commas, double-quotes, or newlines must be enclosed in double-quotes, and internal double-quotes must be doubled.
Proposed fix
- # Escape CSV fields
- job_name="${job_name//,/\\,}"
- run_name="${run_name//,/\\,}"
- state_message="${state_message//,/\\,}"
-
- printf "%s,%s,%s,%s,%s,%s,%s\n" \
- "$job_id" "$job_name" "$run_name" "$formatted_date" "$formatted_duration" "$triggering" "$state_message"
+ # Proper RFC 4180 CSV escaping
+ csv_escape() { local v="${1//\"/\"\"}"; printf '"%s"' "$v"; }
+ printf "%s,%s,%s,%s,%s,%s,%s\n" \
+ "$job_id" "$(csv_escape "$job_name")" "$(csv_escape "$run_name")" "$formatted_date" "$formatted_duration" "$triggering" "$(csv_escape "$state_message")"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Escape CSV fields | |
| job_name="${job_name//,/\\,}" | |
| run_name="${run_name//,/\\,}" | |
| state_message="${state_message//,/\\,}" | |
| printf "%s,%s,%s,%s,%s,%s,%s\n" \ | |
| "$job_id" "$job_name" "$run_name" "$formatted_date" "$formatted_duration" "$triggering" "$state_message" | |
| done | |
| # Proper RFC 4180 CSV escaping | |
| csv_escape() { local v="${1//\"/\"\"}"; printf '"%s"' "$v"; } | |
| printf "%s,%s,%s,%s,%s,%s,%s\n" \ | |
| "$job_id" "$(csv_escape "$job_name")" "$(csv_escape "$run_name")" "$formatted_date" "$formatted_duration" "$triggering" "$(csv_escape "$state_message")" | |
| done |
🤖 Prompt for AI Agents
In
`@plugins/databricks-devtools/skills/databricks-jobs/scripts/list-failed-runs.sh`
around lines 360 - 367, The current CSV escaping replacing commas with
backslashes is invalid; update the logic that prepares job_name, run_name,
state_message (and any other printed fields like job_id or formatted_date) to
follow RFC 4180: write a helper (e.g., csv_escape_field) that, for a given
string, doubles any internal double-quotes and wraps the entire field in
double-quotes if it contains a comma, double-quote, or newline, then call that
helper for job_name, run_name, state_message (and other fields as needed) before
the printf so the printed CSV uses proper quoting.
| local cutoff | ||
| cutoff=$(get_cutoff_timestamp) | ||
| log_debug "Cutoff timestamp: $cutoff" |
There was a problem hiding this comment.
log_debug is not defined — this will crash at runtime.
Line 395 calls log_debug which doesn't exist anywhere in the script. Under set -euo pipefail, this will cause an immediate exit with a "command not found" error.
Proposed fix
- log_debug "Cutoff timestamp: $cutoff"
+ log_info "Cutoff timestamp: $cutoff"Or define log_debug alongside the other logging functions.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| local cutoff | |
| cutoff=$(get_cutoff_timestamp) | |
| log_debug "Cutoff timestamp: $cutoff" | |
| local cutoff | |
| cutoff=$(get_cutoff_timestamp) | |
| log_info "Cutoff timestamp: $cutoff" |
🤖 Prompt for AI Agents
In
`@plugins/databricks-devtools/skills/databricks-jobs/scripts/list-failed-runs.sh`
around lines 393 - 395, The script calls log_debug after computing
cutoff=$(get_cutoff_timestamp) but log_debug is not defined (with set -euo
pipefail this will abort); either add a log_debug function alongside the other
logging helpers (matching their signature and behavior) or change the call to
use an existing defined logger (e.g., log_info/log_verbose) and ensure that
logger is available in the script; update references around get_cutoff_timestamp
to use the chosen, defined logging function so the script no longer tries to
invoke an undefined command.
| main() { | ||
| parse_args "$@" | ||
| start_job | ||
| poll_status | ||
| local exit_code=$? | ||
|
|
||
| if [[ $exit_code -ne 0 ]]; then | ||
| display_output | ||
| fi | ||
|
|
||
| exit $exit_code | ||
| } |
There was a problem hiding this comment.
set -e causes the script to exit before capturing poll_status exit code.
With set -euo pipefail, when poll_status returns a non-zero exit code (1 for failure, 2 for timeout), the shell terminates immediately. Line 288 (local exit_code=$?) is never reached, so display_output is never called on failure — defeating its purpose.
Proposed fix
parse_args "$@"
start_job
- poll_status
- local exit_code=$?
+ local exit_code=0
+ poll_status || exit_code=$?
if [[ $exit_code -ne 0 ]]; then
display_output
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| main() { | |
| parse_args "$@" | |
| start_job | |
| poll_status | |
| local exit_code=$? | |
| if [[ $exit_code -ne 0 ]]; then | |
| display_output | |
| fi | |
| exit $exit_code | |
| } | |
| main() { | |
| parse_args "$@" | |
| start_job | |
| local exit_code=0 | |
| poll_status || exit_code=$? | |
| if [[ $exit_code -ne 0 ]]; then | |
| display_output | |
| fi | |
| exit $exit_code | |
| } |
🤖 Prompt for AI Agents
In `@plugins/databricks-devtools/skills/databricks-jobs/scripts/run-and-wait.sh`
around lines 284 - 295, The script currently uses set -euo pipefail which causes
the shell to exit immediately if poll_status returns non-zero, so main() never
reaches "local exit_code=$?" and display_output isn't called; modify main() to
temporarily disable errexit before invoking poll_status (for example use "set
+e" or call "poll_status || true"), capture its exit code into exit_code, then
restore errexit (e.g., "set -e") and proceed to call display_output when
exit_code != 0; reference the main function and the poll_status and
display_output symbols when making the change.
| set -euo pipefail | ||
|
|
||
| PROFILE="${DATABRICKS_PROFILE:-alpha}" | ||
| USER=$(databricks --profile "$PROFILE" current-user me --output json | jq -r '.userName') |
There was a problem hiding this comment.
Line 18: Shadows built-in USER and masks return value (SC2155).
Overwriting USER shadows the shell's built-in environment variable, which could cause subtle issues. Also, combining declaration and assignment masks the exit code of the databricks command under set -e.
Proposed fix
-USER=$(databricks --profile "$PROFILE" current-user me --output json | jq -r '.userName')
+DB_USER=""
+DB_USER=$(databricks --profile "$PROFILE" current-user me --output json | jq -r '.userName')Then replace all subsequent $USER references with $DB_USER.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| set -euo pipefail | |
| PROFILE="${DATABRICKS_PROFILE:-alpha}" | |
| USER=$(databricks --profile "$PROFILE" current-user me --output json | jq -r '.userName') | |
| set -euo pipefail | |
| PROFILE="${DATABRICKS_PROFILE:-alpha}" | |
| DB_USER="" | |
| DB_USER=$(databricks --profile "$PROFILE" current-user me --output json | jq -r '.userName') |
🤖 Prompt for AI Agents
In
`@plugins/databricks-devtools/skills/databricks-workspace/examples/databricks-tools.sh`
around lines 15 - 18, The script shadows the shell BUILTIN variable USER and
masks the databricks command exit code; rename USER to DB_USER and capture the
databricks output in a separate command so the exit status isn't masked.
Specifically, replace the USER variable reference with DB_USER, run the
databricks --profile "$PROFILE" current-user me --output json command on its own
line (capture its output into a temporary variable or check its exit code), then
pipe that output to jq -r '.userName' to assign DB_USER; update all subsequent
$USER references to $DB_USER and keep PROFILE as-is.
| # Run code immediately | ||
| run() { | ||
| local code="${1:-print('Hello from Databricks')}" | ||
| local cluster | ||
| cluster=$(get_cluster) | ||
| local temp="/Users/$USER/.temp/run_$(date +%s)" | ||
|
|
||
| databricks --profile "$PROFILE" workspace mkdirs "/Users/$USER/.temp" >/dev/null 2>&1 || true | ||
| echo "# Databricks notebook source" > /tmp/nb.py | ||
| echo "$code" >> /tmp/nb.py | ||
|
|
||
| databricks --profile "$PROFILE" workspace import "$temp" --file /tmp/nb.py --language PYTHON --overwrite >/dev/null | ||
| databricks --profile "$PROFILE" jobs submit --json "{\"run_name\":\"run\",\"tasks\":[{\"task_key\":\"t\",\"notebook_task\":{\"notebook_path\":\"$temp\"},\"existing_cluster_id\":\"$cluster\"}]}" -o json | \ | ||
| jq -c '{run_id,result:.tasks[0].state.result_state,duration:.tasks[0].execution_duration}' | ||
|
|
||
| databricks --profile "$PROFILE" workspace delete "$temp" >/dev/null 2>&1 | ||
| rm /tmp/nb.py | ||
| } |
There was a problem hiding this comment.
Multiple issues in run(): hardcoded temp file, missing error handling, and reading result before job completes.
- Hardcoded
/tmp/nb.py(lines 37–38): Concurrent invocations will clobber each other. Use a unique name (e.g.,mktemp). jobs submitresult is read immediately (lines 41–42): The.tasks[0].state.result_statewill benullor empty right after submission since the job hasn't finished yet. There's no wait/poll loop here unlikerun-simple.sh.- Line 44:
workspace deletewithout|| truewill abort underset -eif cleanup fails. - Line 45:
rm /tmp/nb.pywithout-fwill fail underset -eif the file was already removed.
Proposed fix (partial — temp file and cleanup)
- echo "# Databricks notebook source" > /tmp/nb.py
- echo "$code" >> /tmp/nb.py
+ local temp_file
+ temp_file=$(mktemp /tmp/nb_XXXXXX.py)
+ echo "# Databricks notebook source" > "$temp_file"
+ echo "$code" >> "$temp_file"
- databricks --profile "$PROFILE" workspace import "$temp" --file /tmp/nb.py --language PYTHON --overwrite >/dev/null
+ databricks --profile "$PROFILE" workspace import "$temp" --file "$temp_file" --language PYTHON --overwrite >/dev/null
...
- databricks --profile "$PROFILE" workspace delete "$temp" >/dev/null 2>&1
- rm /tmp/nb.py
+ databricks --profile "$PROFILE" workspace delete "$temp" >/dev/null 2>&1 || true
+ rm -f "$temp_file"The missing wait/poll for job completion (issue #2) is a functional correctness problem — the script will always report null for result state. Consider adding a polling loop similar to run-simple.sh.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Run code immediately | |
| run() { | |
| local code="${1:-print('Hello from Databricks')}" | |
| local cluster | |
| cluster=$(get_cluster) | |
| local temp="/Users/$USER/.temp/run_$(date +%s)" | |
| databricks --profile "$PROFILE" workspace mkdirs "/Users/$USER/.temp" >/dev/null 2>&1 || true | |
| echo "# Databricks notebook source" > /tmp/nb.py | |
| echo "$code" >> /tmp/nb.py | |
| databricks --profile "$PROFILE" workspace import "$temp" --file /tmp/nb.py --language PYTHON --overwrite >/dev/null | |
| databricks --profile "$PROFILE" jobs submit --json "{\"run_name\":\"run\",\"tasks\":[{\"task_key\":\"t\",\"notebook_task\":{\"notebook_path\":\"$temp\"},\"existing_cluster_id\":\"$cluster\"}]}" -o json | \ | |
| jq -c '{run_id,result:.tasks[0].state.result_state,duration:.tasks[0].execution_duration}' | |
| databricks --profile "$PROFILE" workspace delete "$temp" >/dev/null 2>&1 | |
| rm /tmp/nb.py | |
| } | |
| # Run code immediately | |
| run() { | |
| local code="${1:-print('Hello from Databricks')}" | |
| local cluster | |
| cluster=$(get_cluster) | |
| local temp="/Users/$USER/.temp/run_$(date +%s)" | |
| databricks --profile "$PROFILE" workspace mkdirs "/Users/$USER/.temp" >/dev/null 2>&1 || true | |
| local temp_file | |
| temp_file=$(mktemp /tmp/nb_XXXXXX.py) | |
| echo "# Databricks notebook source" > "$temp_file" | |
| echo "$code" >> "$temp_file" | |
| databricks --profile "$PROFILE" workspace import "$temp" --file "$temp_file" --language PYTHON --overwrite >/dev/null | |
| databricks --profile "$PROFILE" jobs submit --json "{\"run_name\":\"run\",\"tasks\":[{\"task_key\":\"t\",\"notebook_task\":{\"notebook_path\":\"$temp\"},\"existing_cluster_id\":\"$cluster\"}]}" -o json | \ | |
| jq -c '{run_id,result:.tasks[0].state.result_state,duration:.tasks[0].execution_duration}' | |
| databricks --profile "$PROFILE" workspace delete "$temp" >/dev/null 2>&1 || true | |
| rm -f "$temp_file" | |
| } |
🧰 Tools
🪛 Shellcheck (0.11.0)
[warning] 34-34: Declare and assign separately to avoid masking return values.
(SC2155)
🤖 Prompt for AI Agents
In
`@plugins/databricks-devtools/skills/databricks-workspace/examples/databricks-tools.sh`
around lines 29 - 46, In run(), avoid the hardcoded /tmp/nb.py by creating a
unique temp notebook file (use mktemp or similar) and reference that path when
creating / importing the notebook; after databricks jobs submit, do not read
.tasks[0].state.result_state immediately—instead implement a polling/wait loop
(similar to run-simple.sh) that polls the run status until completion and then
reads the final result_state and execution_duration from the completed run; make
workspace delete resilient by appending || true to the databricks workspace
delete call (referencing workspace delete "$temp") and make the local cleanup
robust by using rm -f on the temp file path; ensure errors from databricks
commands are handled or propagated appropriately so failures don't produce
misleading null results.
| databricks --profile "$PROFILE" repos list --output json | \ | ||
| jq -r '.[] | "\(.path | split("/") | .[-1]) - \(.url)' 2>/dev/null || \ | ||
| log "No repos found or repos API not available" | ||
| echo |
There was a problem hiding this comment.
Syntax error in jq expression — missing closing double quote.
The jq filter string is missing a closing ", which will cause a jq parse error at runtime.
Proposed fix
databricks --profile "$PROFILE" repos list --output json | \
- jq -r '.[] | "\(.path | split("/") | .[-1]) - \(.url)' 2>/dev/null || \
+ jq -r '.[] | "\(.path | split("/") | .[-1]) - \(.url)"' 2>/dev/null || \
log "No repos found or repos API not available"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| databricks --profile "$PROFILE" repos list --output json | \ | |
| jq -r '.[] | "\(.path | split("/") | .[-1]) - \(.url)' 2>/dev/null || \ | |
| log "No repos found or repos API not available" | |
| echo | |
| databricks --profile "$PROFILE" repos list --output json | \ | |
| jq -r '.[] | "\(.path | split("/") | .[-1]) - \(.url)"' 2>/dev/null || \ | |
| log "No repos found or repos API not available" | |
| echo |
🤖 Prompt for AI Agents
In
`@plugins/databricks-devtools/skills/databricks-workspace/examples/quick-start.sh`
around lines 160 - 163, The jq filter string in the pipeline that starts with
the databricks repos list command is missing a closing double quote, causing a
jq parse error; fix it by adding the missing closing double quote at the end of
the jq filter passed to jq -r so the expression inside the single quotes becomes
a properly balanced string (the pipeline is the one starting with databricks
--profile "$PROFILE" repos list --output json | jq -r and producing '.[] |
"\(.path | split("/") | .[-1]) - \(.url)').
| if [ "$final_state" != "TERMINATED" ]; then | ||
| error "Job did not complete successfully (state: $final_state)" | ||
| else | ||
| result_state=$(databricks --profile "$PROFILE" jobs get-run "$run_id" --output json | \ | ||
| jq -r '.tasks[0].state.result_state') | ||
|
|
||
| case "$result_state" in | ||
| SUCCESS) | ||
| log "Job completed successfully" | ||
| ;; | ||
| FAILED|TIMEDOUT|CANCELED) | ||
| error "Job $result_state" | ||
| ;; | ||
| *) | ||
| log "Job result: $result_state" | ||
| ;; | ||
| esac | ||
| fi | ||
|
|
||
| # Cleanup | ||
| databricks --profile "$PROFILE" workspace delete "$temp_path" >/dev/null 2>&1 || true | ||
| rm -f "$temp_file" | ||
|
|
||
| return 0 |
There was a problem hiding this comment.
run_code returns 0 even when the job fails.
Lines 204–206 log an error for FAILED|TIMEDOUT|CANCELED and line 195 logs an error for non-TERMINATED states, but the function always falls through to return 0 on line 217. Callers (and set -e) won't detect a failed execution.
Proposed fix
+ local exit_code=0
+
if [ "$final_state" != "TERMINATED" ]; then
error "Job did not complete successfully (state: $final_state)"
+ exit_code=1
else
result_state=$(databricks --profile "$PROFILE" jobs get-run "$run_id" --output json | \
jq -r '.tasks[0].state.result_state')
case "$result_state" in
SUCCESS)
log "Job completed successfully"
;;
FAILED|TIMEDOUT|CANCELED)
error "Job $result_state"
+ exit_code=1
;;
*)
log "Job result: $result_state"
;;
esac
fi
# Cleanup
databricks --profile "$PROFILE" workspace delete "$temp_path" >/dev/null 2>&1 || true
rm -f "$temp_file"
- return 0
+ return "$exit_code"
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if [ "$final_state" != "TERMINATED" ]; then | |
| error "Job did not complete successfully (state: $final_state)" | |
| else | |
| result_state=$(databricks --profile "$PROFILE" jobs get-run "$run_id" --output json | \ | |
| jq -r '.tasks[0].state.result_state') | |
| case "$result_state" in | |
| SUCCESS) | |
| log "Job completed successfully" | |
| ;; | |
| FAILED|TIMEDOUT|CANCELED) | |
| error "Job $result_state" | |
| ;; | |
| *) | |
| log "Job result: $result_state" | |
| ;; | |
| esac | |
| fi | |
| # Cleanup | |
| databricks --profile "$PROFILE" workspace delete "$temp_path" >/dev/null 2>&1 || true | |
| rm -f "$temp_file" | |
| return 0 | |
| local exit_code=0 | |
| if [ "$final_state" != "TERMINATED" ]; then | |
| error "Job did not complete successfully (state: $final_state)" | |
| exit_code=1 | |
| else | |
| result_state=$(databricks --profile "$PROFILE" jobs get-run "$run_id" --output json | \ | |
| jq -r '.tasks[0].state.result_state') | |
| case "$result_state" in | |
| SUCCESS) | |
| log "Job completed successfully" | |
| ;; | |
| FAILED|TIMEDOUT|CANCELED) | |
| error "Job $result_state" | |
| exit_code=1 | |
| ;; | |
| *) | |
| log "Job result: $result_state" | |
| ;; | |
| esac | |
| fi | |
| # Cleanup | |
| databricks --profile "$PROFILE" workspace delete "$temp_path" >/dev/null 2>&1 || true | |
| rm -f "$temp_file" | |
| return "$exit_code" |
🤖 Prompt for AI Agents
In
`@plugins/databricks-devtools/skills/databricks-workspace/examples/run-simple.sh`
around lines 194 - 217, The function run_code always returns 0 even when the
Databricks job fails; update its control flow to propagate failure to callers by
returning a non-zero exit code: introduce an exit_code variable (default 0) in
run_code, set exit_code=1 when final_state != "TERMINATED" or when result_state
matches FAILED|TIMEDOUT|CANCELED (the branches that call error), and replace the
unconditional return 0 with return $exit_code so callers and set -e detect
failures; reference variables final_state and result_state and the run_id
handling when locating the change.
Summary
This PR fixes the empty referenced directories issue identified during Databricks skills testing. All example files, reference documentation, and scripts promised by the SKILL.md files have been created.
Changes
databricks-cli
databricks-workspace
databricks-jobs
Quality Improvements
set -euo pipefail)Testing
All scripts:
#!/usr/bin/env bash)set -euo pipefailfor error handlingBreaking Changes
None.
Related Issues
Fixes empty referenced directories issue found during Databricks skills testing.
Summary by CodeRabbit
Release Notes
Documentation
Examples & Tools