From 41f58faca0ddac855f0a01254b9c8b7770444e0b Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Sun, 18 Oct 2026 14:32:43 +0200 Subject: fix(weather): fetcher review fixes - quote CITY in weather.env.example and note why - re-geocode when weather-loc lacks a longitude; write it atomically - parse the air body with --rawfile/fromjson? so a bad body drops only the air line - set umask before mkdir so the cache dir is 700 - drop the unneeded set -a/set +a around the source - require at least one digit in the epoch guard Co-Authored-By: Claude Sonnet 5.5 --- bin/weather-fetch.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'bin/weather-fetch.sh') diff --git a/bin/weather-fetch.sh b/bin/weather-fetch.sh index 4b37b17..e9f205b 100755 --- a/bin/weather-fetch.sh +++ b/bin/weather-fetch.sh @@ -21,10 +21,8 @@ if [ ! -r "$ENV_FILE" ]; then exit 1 fi -set -a # shellcheck source=/dev/null . "$ENV_FILE" -set +a if [ -z "${CITY:-}" ]; then echo "weather-fetch: CITY is empty in $ENV_FILE" >&2 @@ -32,20 +30,21 @@ if [ -z "${CITY:-}" ]; then fi UNITS="${UNITS:-metric}" -mkdir -p "$CACHE_DIR" - # Location data shares a directory with UDT's other generated state, all of -# which is 600. Set the mode before anything lands, so no file is briefly +# which is 600. Set the mode before anything lands, the +# directory included, so no file is briefly # world-readable. umask 077 +mkdir -p "$CACHE_DIR" + # Temp files sit beside the target, because rename is only atomic within a # filesystem. The widgets read the cache on their own 2s cadence, so a fetch # killed mid-write would otherwise hand them half a cache. TMP="$CACHE.tmp.$$" FC="$TMP.forecast" AIR="$TMP.air" -trap 'rm -f "$TMP" "$FC" "$AIR"' EXIT +trap 'rm -f "$TMP" "$FC" "$AIR" "$LOC.tmp.$$"' EXIT # Geocode once per CITY/COUNTRY pair. The cache's first field records which # pair it answers, so editing weather.env re-geocodes on the next run. @@ -53,7 +52,7 @@ PAIR="$CITY,${COUNTRY:-}" lat="" lon="" cached="" if [ -r "$LOC" ]; then IFS=$'\t' read -r cached lat lon < "$LOC" - [ "$cached" = "$PAIR" ] || lat="" + [ "$cached" = "$PAIR" ] && [ -n "$lon" ] || lat="" fi if [ -z "$lat" ]; then read -r lat lon < <(curl -fsS --max-time 15 --get \ @@ -66,7 +65,8 @@ if [ -z "$lat" ]; then echo "weather-fetch: cannot geocode $PAIR" >&2 exit 1 fi - printf '%s\t%s\t%s\n' "$PAIR" "$lat" "$lon" > "$LOC" + printf '%s\t%s\t%s\n' "$PAIR" "$lat" "$lon" > "$LOC.tmp.$$" + mv -f "$LOC.tmp.$$" "$LOC" fi if [ "$UNITS" = imperial ]; then @@ -98,13 +98,13 @@ curl -fsS --max-time 15 --get "https://air-quality-api.open-meteo.com/v1/air-qua # Flatten to the line format lib/weather.lua parses. The 24 hours start at # the current hour: the first hourly slot later than an hour before now. -if ! jq -r --arg units "$UNITS" --slurpfile air "$AIR" ' +if ! jq -r --arg units "$UNITS" --rawfile air "$AIR" ' .current.time as $now | ([.hourly.time | to_entries[] | select(.value > $now - 3600) | .key] | first) as $i | "units\t\($units)", (.current | "current\t\(.time)\t\(.temperature_2m)\t\(.apparent_temperature)\t\(.weather_code)\t\(.is_day)\t\(.relative_humidity_2m)\t\(.wind_speed_10m)\t\(.wind_direction_10m)"), "sun\t\(.daily.sunrise[0])\t\(.daily.sunset[0])", - ($air[0].current.european_aqi // empty | "air\t\(.)"), + (($air | fromjson?) | .current.european_aqi // empty | "air\t\(.)"), (.hourly as $h | range($i; [$i + 24, ($h.time | length)] | min) as $k | "hour\t\($h.time[$k])\t\($h.temperature_2m[$k])\t\($h.weather_code[$k])\t\($h.precipitation_probability[$k])\t\($h.is_day[$k])\t\($h.uv_index[$k])"), (.daily as $d | range(0; $d.time | length) as $k @@ -115,7 +115,7 @@ if ! jq -r --arg units "$UNITS" --slurpfile air "$AIR" ' fi # A 200 whose body is not a usable forecast must not replace a good cache. -if ! grep -q $'^current\t[0-9]*\t-\\?[0-9]' "$TMP"; then +if ! grep -q $'^current\t[0-9]\+\t-\\?[0-9]' "$TMP"; then echo "weather-fetch: forecast has no current temperature" >&2 exit 1 fi -- cgit v1.2.3