From 279b0320b7a5fc3bba5d0c6187b2361bcd4cd008 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Mon, 10 Nov 2025 21:41:29 +0100 Subject: [PATCH] Improve README updater workflow with yq Refactor README updater workflow to use yq for YAML processing and simplify config handling. --- .github/workflows/daily_README.yaml | 208 ++++++++++++---------------- 1 file changed, 90 insertions(+), 118 deletions(-) diff --git a/.github/workflows/daily_README.yaml b/.github/workflows/daily_README.yaml index d1a5f4e8f..59e9e8517 100644 --- a/.github/workflows/daily_README.yaml +++ b/.github/workflows/daily_README.yaml @@ -5,6 +5,7 @@ on: schedule: - cron: 0 17 * * * workflow_dispatch: null + jobs: README_updater: if: github.repository_owner == 'alexbelgium' @@ -12,135 +13,112 @@ jobs: steps: - name: Checkout Repo uses: actions/checkout@v5 + + - name: Install yq (and jq) + run: | + sudo apt-get update + sudo apt-get install -y jq + YQ_VERSION="v4.44.3" + sudo wget -qO /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" + sudo chmod +x /usr/local/bin/yq + - name: Create README file run: | # Init - echo "Starting" - # Ensure dependencies available - echo "Installing PyYAML" - python3 -m pip install --user --quiet PyYAML - export PATH="$HOME/.local/bin:$PATH" - - get_config_file() { - if [ -f "$1/config.json" ]; then - echo "$1/config.json" - elif [ -f "$1/config.yaml" ]; then - echo "$1/config.yaml" - else - echo "" - fi - } - - convert_config_to_json() { - local source="$1" - if [[ "$source" == *.yaml ]]; then - local temp_file - temp_file="$(mktemp)" - python3 - <<'PY' "$source" "$temp_file" -import json -import sys - -from pathlib import Path - -import yaml - -source = Path(sys.argv[1]) -target = Path(sys.argv[2]) - -with source.open("r", encoding="utf-8") as fp: - data = yaml.safe_load(fp) - -with target.open("w", encoding="utf-8") as fp: - json.dump(data, fp) -PY - echo "$temp_file" - else - echo "$source" - fi - } - # Prepare template - cp .templates/.README.md README2.md - ADDONSLINE="$(sed -n '/%%ADDONS_LIST%%/=' README2.md)" + sed -i "/%%ADDONS_LIST%%/d" README2.md - sed -i "/**ADDONS_LIST%%/d" README2.md + # Helper to find a config file (json/yaml/yml) + get_cfg() { + local dir="$1" + for ext in json yaml yml; do + if [ -f "$dir/config.$ext" ]; then + echo "$dir/config.$ext" + return 0 + fi + done + return 1 + } - # Sort folders by addon name - - # shellcheck disable=SC2086 + # Sort folders by addon name (support json & yaml) + # shellcheck disable=SC2046 for f in $( find -- * -maxdepth 0 -type d | sort -r ); do - CONFIG_SRC="$(get_config_file "$f")" - if [ -n "$CONFIG_SRC" ]; then - CONFIG_FILE="$(convert_config_to_json "$CONFIG_SRC")" - NAME=$(jq -r '.name' "$CONFIG_FILE") - if [[ "$f" != "$NAME" ]]; then - echo "$f" > "$f"/oldname + CFG="$(get_cfg "$f" || true)" + if [ -n "${CFG:-}" ]; then + NAME="$(yq -r '.name // ""' "$CFG")" + if [ -n "$NAME" ] && [ "$f" != "$NAME" ]; then + echo "$f" > "$f/oldname" mv "$f" "$NAME" fi - if [[ "$CONFIG_FILE" != "$CONFIG_SRC" ]]; then rm "$CONFIG_FILE"; fi fi done - # Populate template - find -- * -maxdepth 0 -type d | sort -r | while read -r f; do - # $f is an addon directory - CONFIG_SRC="$(get_config_file "$f")" - if [ -n "$CONFIG_SRC" ]; then + CFG="$(get_cfg "$f" || true)" + [ -n "${CFG:-}" ] || continue - CONFIG_FILE="$(convert_config_to_json "$CONFIG_SRC")" + echo "Project $f" - echo "Project $f" + # Folder name in repo root (for links/badges) + if [ -f "$f/oldname" ]; then FOLDERNAME="$(cat "$f/oldname")"; else FOLDERNAME="$f"; fi - # Get variables - if [ -f "$f/oldname" ]; then FOLDERNAME="$(cat "$f/oldname")"; else FOLDERNAME="$f"; fi - NAME="$(jq -r '.name' "$CONFIG_FILE")" - DESCRIPTION="$(jq -r '.description' "$CONFIG_FILE")" - # Get icon - if [ "$(jq '.panel_icon' "$CONFIG_FILE")" != null ]; then - ICON="$(jq -r '.panel_icon' "$CONFIG_FILE")" - ICON="${ICON#*:}" - ICON="![image](https://api.iconify.design/mdi/$ICON.svg)" - else - ICON="" - fi + # Choose badge type & extension for Shields + EXT="${CFG##*.}" + BADGE_KIND="json" + case "$EXT" in + yaml|yml) BADGE_KIND="yaml" ;; + esac - # Write infos - echo "Writing infos" - sed -i "$ADDONSLINE"'{G;}' README2.md - if [[ "$(jq '.schema' "$CONFIG_FILE" 2>/dev/null)" == *"localdisks"* ]]; then sed -i "$ADDONSLINE"'a ![localdisks][localdisks-badge]' README2.md; fi - if [[ "$(jq '.schema' "$CONFIG_FILE" 2>/dev/null)" == *"networkdisks"* ]]; then sed -i "$ADDONSLINE"'a ![smb][smb-badge]' README2.md; fi - if [[ "$(jq '.full_access' "$CONFIG_FILE" 2>/dev/null)" == "true" ]]; then sed -i "$ADDONSLINE"'a ![full_access][full_access-badge]' README2.md; fi - if [[ "$(jq '.services[]' "$CONFIG_FILE" 2>/dev/null)" == *"mqtt"* ]]; then sed -i "$ADDONSLINE"'a ![mqtt][mqtt-badge]' README2.md; fi - if [[ "$(jq '.services[]' "$CONFIG_FILE" 2>/dev/null)" == *"mysql"* ]]; then sed -i "$ADDONSLINE"'a ![MariaDB][mariadb-badge]' README2.md; fi - if [[ "$(jq '.ingress' "$CONFIG_FILE" 2>/dev/null)" == "true" ]]; then sed -i "$ADDONSLINE"'a ![ingress][ingress-badge]' README2.md; fi - if [[ "$(jq '.arch[]' "$CONFIG_FILE")" == *"armv7"* ]]; then - sed -i "$ADDONSLINE"'a ![armv7][armv7-badge]' README2.md - else sed -i "$ADDONSLINE"'a ![armv7no][armv7no-badge]' README2.md; fi || true - if [[ "$(jq '.arch[]' "$CONFIG_FILE")" == *"amd64"* ]]; then - sed -i "$ADDONSLINE"'a ![amd64][amd64-badge]' README2.md - else sed -i "$ADDONSLINE"'a ![amd64no][amd64no-badge]' README2.md; fi || true - if [[ "$(jq '.arch[]' "$CONFIG_FILE")" == *"aarch64"* ]]; then - sed -i "$ADDONSLINE"'a ![aarch64][aarch64-badge]' README2.md - else sed -i "$ADDONSLINE"'a ![aarch64no][aarch64no-badge]' README2.md; fi || true - if [[ -f "$f/updater.json" ]]; then sed -i "$ADDONSLINE"'a ![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2F'"$FOLDERNAME"'%2Fupdater.json)' README2.md; fi - if [[ "$CONFIG_SRC" == *.yaml ]]; then - sed -i "$ADDONSLINE"'a   ![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2F'"$FOLDERNAME"'%2Fconfig.yaml)' README2.md || true - else - sed -i "$ADDONSLINE"'a   ![Version](https://img.shields.io/badge/dynamic/json?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2F'"$FOLDERNAME"'%2Fconfig.json)' README2.md || true - fi - sed -i "$ADDONSLINE"'a ✓ '"$ICON"' ['"$NAME"']('"$FOLDERNAME"'/) : '"$DESCRIPTION\\n" README2.md - if [[ "$CONFIG_FILE" != "$CONFIG_SRC" ]]; then rm "$CONFIG_FILE"; fi + # Read fields using yq (works for both JSON & YAML) + NAME="$(yq -r '.name // ""' "$CFG")" + DESCRIPTION="$(yq -r '.description // ""' "$CFG")" + PANEL_ICON="$(yq -r '.panel_icon // ""' "$CFG")" + + # Icon (mdi:foo) + if [ -n "$PANEL_ICON" ] && [ "$PANEL_ICON" != "null" ]; then + ICON_NAME="${PANEL_ICON#*:}" + ICON="![image](https://api.iconify.design/mdi/$ICON_NAME.svg)" + else + ICON="" fi + + # Derived strings for checks + SCHEMA_STR="$(yq -o=json '.schema // {}' "$CFG" | tr -d '\n' || true)" + SERVICES="$(yq -r '.services[]? // empty' "$CFG" | tr '\n' ' ' || true)" + ARCHS="$(yq -r '.arch[]? // empty' "$CFG" | tr '\n' ' ' || true)" + FULL_ACCESS="$(yq -r '.full_access // false' "$CFG" || true)" + INGRESS="$(yq -r '.ingress // false' "$CFG" || true)" + + # Write infos + echo "Writing infos" + sed -i "$ADDONSLINE"'{G;}' README2.md + if [[ "$SCHEMA_STR" == *"localdisks"* ]]; then sed -i "$ADDONSLINE"'a ![localdisks][localdisks-badge]' README2.md; fi + if [[ "$SCHEMA_STR" == *"networkdisks"* ]]; then sed -i "$ADDONSLINE"'a ![smb][smb-badge]' README2.md; fi + if [[ "$FULL_ACCESS" == "true" ]]; then sed -i "$ADDONSLINE"'a ![full_access][full_access-badge]' README2.md; fi + if [[ "$SERVICES" == *"mqtt"* ]]; then sed -i "$ADDONSLINE"'a ![mqtt][mqtt-badge]' README2.md; fi + if [[ "$SERVICES" == *"mysql"* ]]; then sed -i "$ADDONSLINE"'a ![MariaDB][mariadb-badge]' README2.md; fi + if [[ "$INGRESS" == "true" ]]; then sed -i "$ADDONSLINE"'a ![ingress][ingress-badge]' README2.md; fi + + if [[ " $ARCHS " == *" armv7 "* ]]; then sed -i "$ADDONSLINE"'a ![armv7][armv7-badge]' README2.md; else sed -i "$ADDONSLINE"'a ![armv7no][armv7no-badge]' README2.md; fi || true + if [[ " $ARCHS " == *" amd64 "* ]]; then sed -i "$ADDONSLINE"'a ![amd64][amd64-badge]' README2.md; else sed -i "$ADDONSLINE"'a ![amd64no][amd64no-badge]' README2.md; fi || true + if [[ " $ARCHS " == *" aarch64 "* ]]; then sed -i "$ADDONSLINE"'a ![aarch64][aarch64-badge]' README2.md; else sed -i "$ADDONSLINE"'a ![aarch64no][aarch64no-badge]' README2.md; fi || true + + if [[ -f "$f/updater.json" ]]; then + sed -i "$ADDONSLINE"'a ![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2F'"$FOLDERNAME"'%2Fupdater.json)' README2.md + fi + + # Version badge: picks dynamic/json for JSON, dynamic/yaml for YAML + sed -i "$ADDONSLINE"'a   ![Version](https://img.shields.io/badge/dynamic/'"$BADGE_KIND"'?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2F'"$FOLDERNAME"'%2Fconfig.'"$EXT"')' README2.md || true + + sed -i "$ADDONSLINE"'a ✓ '"$ICON"' ['"$NAME"']('"$FOLDERNAME"'/) : '"$DESCRIPTION\\n" README2.md done # Restore folders name - echo "Restore structure..." find -- * -maxdepth 0 -type d | sort -r | while read -r f; do if [ -f "$f/oldname" ]; then @@ -152,13 +130,10 @@ PY echo "... done" # Write stats - - # shellcheck disable=SC2002 echo "Global stats..." STATS_DOWNLOADS="$(awk 'NR==2{print $1}' Stats)" sed -i "s|%%STATS_DOWNLOADS%%|$STATS_DOWNLOADS|g" README2.md && \ - ADDONS_COUNT="$(find -- * -maxdepth 1 -type f \( -name "config.json" -o -name "config.yaml" \) -printf '%h\n' 2>/dev/null | sort -u | wc -l)" && \ - sed -i "s|%%STATS_ADDONS%%|$ADDONS_COUNT|g" README2.md && \ + sed -i "s|%%STATS_ADDONS%%|$(find . -type f \( -name 'config.json' -o -name 'config.yaml' -o -name 'config.yml' \) | wc -l)|g" README2.md && \ STATS_ONE="$(awk 'NR==3{print $(NF)}' Stats)" && \ STATS_TWO="$(awk 'NR==4{print $(NF)}' Stats)" && \ STATS_THREE="$(awk 'NR==5{print $(NF)}' Stats)" @@ -168,44 +143,41 @@ PY sed -i "s|%%STATS_THREE%%|${STATS_THREE^}|g" README2.md echo "... done" - # Breakdown per arch + # Breakdown per arch (unchanged) echo "Breakdown per arch..." STATS_ARMV7="$(awk '{SUM+=$3}END{print SUM}' Stats2)" STATS_AMD64="$(awk '{SUM+=$4}END{print SUM}' Stats2)" STATS_AARCH64="$(awk '{SUM+=$5}END{print SUM}' Stats2)" - STATS_DOWNLOADS="$(( "$STATS_ARMV7" + "$STATS_AMD64" + "$STATS_AARCH64" ))" + STATS_DOWNLOADS="$(( STATS_ARMV7 + STATS_AMD64 + STATS_AARCH64 ))" STATS_ARMV7="$(awk -v t1="$STATS_ARMV7" -v t2="$STATS_AMD64" -v t3="$STATS_AARCH64" -v t4="$STATS_DOWNLOADS" 'BEGIN{printf "%.0f", t1/t4 * 100}')" STATS_AMD64="$(awk -v t1="$STATS_ARMV7" -v t2="$STATS_AMD64" -v t3="$STATS_AARCH64" -v t4="$STATS_DOWNLOADS" 'BEGIN{printf "%.0f", t2/t4 * 100}')" STATS_AARCH64="$(awk -v t1="$STATS_ARMV7" -v t2="$STATS_AMD64" -v t3="$STATS_AARCH64" -v t4="$STATS_DOWNLOADS" 'BEGIN{printf "%.0f", t3/t4 * 100}')" sed -i "s|%%STATS_ARMV7%%|armv7: ${STATS_ARMV7}%|g" README2.md sed -i "s|%%STATS_AMD64%%|amd64: ${STATS_AMD64}%|g" README2.md sed -i "s|%%STATS_AARCH64%%|aarch64: ${STATS_AARCH64}%|g" README2.md - #echo $((100*$STATS_THREE/($STATS_ONE+$STATS_TWO+$STATS_THREE))) echo "... done" for var in "$STATS_ONE" "$STATS_TWO" "$STATS_THREE"; do - i=0 - j=0 - k=0 + i=0 + j=0 + k=0 # shellcheck disable=SC2013 for i in $(sed -n "/$var/p" Stats); do k="$((k+1))" if [ "$k" -eq 3 ]; then break; fi if [ "$i" -eq "$i" ] && [ "$i" -gt "$j" ]; then j="$i"; fi done - sed -i "s|${var^}|${var^} (${j}x)|g" README2.md - echo "$STATS_ONE has $j downloads" + sed -i "s|${var^}|${var^} (${j}x)|g" README2.md + echo "$STATS_ONE has $j downloads" done echo "... done" # Replace template if change - echo "Replace template..." mv README2.md README.md - echo "... done" - shell: bash + - name: Commit if needed uses: EndBug/add-and-commit@v9 with: