bf1efb2099
- Migration 007: repo_url on apps table + analysis table with FTS5 - Analysis struct, parser, CRUD, validation, hash computation - Selective purge: remote-only apps/analysis preserved across fn index - CLI: fn app list/clone/pull, fn analysis list/clone/pull - search/show/list now include analysis results - Apps removed from git tracking (content lives in Gitea repos) - .gitkeep for apps/ and analysis/ dirs - Bash functions: jupyter analysis pipeline, shell utilities - Browser domain: CDP functions moved from infra to browser Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
211 lines
6.8 KiB
Bash
211 lines
6.8 KiB
Bash
#!/usr/bin/env bash
|
|
# report_execution_json — Genera un JSON de reporte de ejecucion siguiendo el estandar fn-registry.
|
|
#
|
|
# USO (sourced):
|
|
# source report_execution_json.sh
|
|
# report_execution_json "backup_db" "partial" 2 \
|
|
# "2026-04-01T02:00:00Z" "2026-04-01T02:00:03Z" 3000 /tmp/steps.tsv
|
|
#
|
|
# USO (ejecutado directamente):
|
|
# bash report_execution_json.sh "backup_db" "partial" 2 \
|
|
# "2026-04-01T02:00:00Z" "2026-04-01T02:00:03Z" 3000 /tmp/steps.tsv
|
|
#
|
|
# FORMATO steps_file (TSV sin cabecera, 6 columnas):
|
|
# name<TAB>action<TAB>status<TAB>elapsed_ms<TAB>output<TAB>error
|
|
# Los campos output y error pueden estar vacios.
|
|
# status valido: ok | error
|
|
#
|
|
# NOTA sobre IFS y tabs: bash trata tab como whitespace en IFS y colapsa
|
|
# campos vacios consecutivos. Se usa 'cut -f N' para parsear cada columna
|
|
# de forma correcta cuando hay campos vacios entre tabs.
|
|
|
|
report_execution_json() {
|
|
local flow_name="$1"
|
|
local status="$2"
|
|
local exit_code="$3"
|
|
local started_at="$4"
|
|
local ended_at="$5"
|
|
local duration_ms="$6"
|
|
local steps_file="$7"
|
|
|
|
if [[ -z "$flow_name" || -z "$status" || -z "$exit_code" || \
|
|
-z "$started_at" || -z "$ended_at" || -z "$duration_ms" || \
|
|
-z "$steps_file" ]]; then
|
|
echo "report_execution_json: uso: report_execution_json <flow_name> <status> <exit_code> <started_at> <ended_at> <duration_ms> <steps_file>" >&2
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -f "$steps_file" ]]; then
|
|
echo "report_execution_json: archivo de pasos no encontrado: $steps_file" >&2
|
|
return 1
|
|
fi
|
|
|
|
if command -v jq >/dev/null 2>&1; then
|
|
_report_execution_json_jq \
|
|
"$flow_name" "$status" "$exit_code" \
|
|
"$started_at" "$ended_at" "$duration_ms" "$steps_file"
|
|
else
|
|
_report_execution_json_printf \
|
|
"$flow_name" "$status" "$exit_code" \
|
|
"$started_at" "$ended_at" "$duration_ms" "$steps_file"
|
|
fi
|
|
}
|
|
|
|
# --- Implementacion con jq ---
|
|
|
|
_report_execution_json_jq() {
|
|
local flow_name="$1" status="$2" exit_code="$3"
|
|
local started_at="$4" ended_at="$5" duration_ms="$6" steps_file="$7"
|
|
|
|
local steps_ok=0 steps_failed=0
|
|
local steps_json="[]"
|
|
|
|
while IFS= read -r line; do
|
|
[[ -z "$line" ]] && continue
|
|
|
|
local s_name s_action s_status s_elapsed s_output s_error
|
|
s_name=$(printf '%s' "$line" | cut -f1)
|
|
s_action=$(printf '%s' "$line" | cut -f2)
|
|
s_status=$(printf '%s' "$line" | cut -f3)
|
|
s_elapsed=$(printf '%s' "$line" | cut -f4)
|
|
s_output=$(printf '%s' "$line" | cut -f5)
|
|
s_error=$(printf '%s' "$line" | cut -f6)
|
|
|
|
[[ -z "$s_name" ]] && continue
|
|
|
|
local step_obj
|
|
step_obj=$(jq -n \
|
|
--arg name "$s_name" \
|
|
--arg action "$s_action" \
|
|
--arg st "$s_status" \
|
|
--argjson ms "${s_elapsed:-0}" \
|
|
--arg output "$s_output" \
|
|
--arg error "$s_error" \
|
|
'{name: $name, action: $action, status: $st, elapsed_ms: $ms}
|
|
+ (if $output != "" then {output: $output} else {} end)
|
|
+ (if $error != "" then {error: $error} else {} end)')
|
|
|
|
steps_json=$(printf '%s' "$steps_json" | jq --argjson step "$step_obj" '. + [$step]')
|
|
|
|
if [[ "$s_status" == "ok" ]]; then
|
|
((steps_ok++))
|
|
else
|
|
((steps_failed++))
|
|
fi
|
|
done < "$steps_file"
|
|
|
|
local steps_total=$(( steps_ok + steps_failed ))
|
|
|
|
jq -n \
|
|
--arg name "$flow_name" \
|
|
--arg st "$status" \
|
|
--argjson exit_code "$exit_code" \
|
|
--arg started_at "$started_at" \
|
|
--arg ended_at "$ended_at" \
|
|
--argjson duration_ms "$duration_ms" \
|
|
--argjson total "$steps_total" \
|
|
--argjson ok "$steps_ok" \
|
|
--argjson failed "$steps_failed" \
|
|
--argjson steps "$steps_json" \
|
|
'{
|
|
name: $name,
|
|
status: $st,
|
|
exit_code: $exit_code,
|
|
started_at: $started_at,
|
|
ended_at: $ended_at,
|
|
duration_ms: $duration_ms,
|
|
steps_total: $total,
|
|
steps_ok: $ok,
|
|
steps_failed: $failed,
|
|
steps: $steps
|
|
}'
|
|
}
|
|
|
|
# --- Implementacion con printf (fallback sin jq) ---
|
|
|
|
_json_escape_rj() {
|
|
local s="$1"
|
|
s="${s//\\/\\\\}"
|
|
s="${s//\"/\\\"}"
|
|
s="${s//$'\n'/\\n}"
|
|
s="${s//$'\r'/\\r}"
|
|
s="${s//$'\t'/\\t}"
|
|
printf '%s' "$s"
|
|
}
|
|
|
|
_report_execution_json_printf() {
|
|
local flow_name="$1" status="$2" exit_code="$3"
|
|
local started_at="$4" ended_at="$5" duration_ms="$6" steps_file="$7"
|
|
|
|
local steps_ok=0 steps_failed=0
|
|
local steps_parts=()
|
|
|
|
while IFS= read -r line; do
|
|
[[ -z "$line" ]] && continue
|
|
|
|
local s_name s_action s_status s_elapsed s_output s_error
|
|
s_name=$(printf '%s' "$line" | cut -f1)
|
|
s_action=$(printf '%s' "$line" | cut -f2)
|
|
s_status=$(printf '%s' "$line" | cut -f3)
|
|
s_elapsed=$(printf '%s' "$line" | cut -f4)
|
|
s_output=$(printf '%s' "$line" | cut -f5)
|
|
s_error=$(printf '%s' "$line" | cut -f6)
|
|
|
|
[[ -z "$s_name" ]] && continue
|
|
|
|
local part
|
|
part=$(printf '{"name":"%s","action":"%s","status":"%s","elapsed_ms":%s' \
|
|
"$(_json_escape_rj "$s_name")" \
|
|
"$(_json_escape_rj "$s_action")" \
|
|
"$(_json_escape_rj "$s_status")" \
|
|
"${s_elapsed:-0}")
|
|
|
|
if [[ -n "$s_output" ]]; then
|
|
part+=",\"output\":\"$(_json_escape_rj "$s_output")\""
|
|
fi
|
|
if [[ -n "$s_error" ]]; then
|
|
part+=",\"error\":\"$(_json_escape_rj "$s_error")\""
|
|
fi
|
|
part+="}"
|
|
|
|
steps_parts+=("$part")
|
|
|
|
if [[ "$s_status" == "ok" ]]; then
|
|
((steps_ok++))
|
|
else
|
|
((steps_failed++))
|
|
fi
|
|
done < "$steps_file"
|
|
|
|
local steps_total=$(( steps_ok + steps_failed ))
|
|
|
|
local steps_array="["
|
|
local first=1
|
|
for part in "${steps_parts[@]}"; do
|
|
if [[ $first -eq 1 ]]; then
|
|
steps_array+="$part"
|
|
first=0
|
|
else
|
|
steps_array+=",$part"
|
|
fi
|
|
done
|
|
steps_array+="]"
|
|
|
|
printf '{"name":"%s","status":"%s","exit_code":%s,"started_at":"%s","ended_at":"%s","duration_ms":%s,"steps_total":%s,"steps_ok":%s,"steps_failed":%s,"steps":%s}\n' \
|
|
"$(_json_escape_rj "$flow_name")" \
|
|
"$(_json_escape_rj "$status")" \
|
|
"$exit_code" \
|
|
"$(_json_escape_rj "$started_at")" \
|
|
"$(_json_escape_rj "$ended_at")" \
|
|
"$duration_ms" \
|
|
"$steps_total" \
|
|
"$steps_ok" \
|
|
"$steps_failed" \
|
|
"$steps_array"
|
|
}
|
|
|
|
# Permitir ejecucion directa (no solo sourced)
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
report_execution_json "$@"
|
|
fi
|