From cd5e8fcac2cb3890d79ad77513a4890ec0c1095b Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Sat, 25 Jul 2026 01:20:16 +0200 Subject: [PATCH] Update ComfyUIdownloads.yml --- .github/workflows/ComfyUIdownloads.yml | 376 ++++++++++++++++--------- 1 file changed, 250 insertions(+), 126 deletions(-) diff --git a/.github/workflows/ComfyUIdownloads.yml b/.github/workflows/ComfyUIdownloads.yml index 1f0d27b..7d2699f 100644 --- a/.github/workflows/ComfyUIdownloads.yml +++ b/.github/workflows/ComfyUIdownloads.yml @@ -1,4 +1,14 @@ -name: Model Resolver Top Downloads Badge +# Reusable workflow +# +# Project metadata is read from pyproject.toml and the Comfy Registry API. +# No project-specific repository variables need to be configured manually. +# The workflow creates and maintains this internal repository variable automatically: +# COMFY_DOWNLOADS_GIST_ID +# +# Required repository secret: +# SECRET_TOKEN GitHub token with Gist and repository Actions variables write access + +name: ComfyUI Top Downloads Badge on: schedule: @@ -8,6 +18,12 @@ on: permissions: contents: write +env: + COMFY_DOWNLOADS_GIST_ID: ${{ vars.COMFY_DOWNLOADS_GIST_ID }} + BADGE_DOC: badges/DOWNLOADS.md + DATA_FILE: comfyui_downloads.json + GH_TOKEN: ${{ secrets.SECRET_TOKEN }} + jobs: build: runs-on: ubuntu-latest @@ -15,39 +31,67 @@ jobs: steps: - uses: actions/checkout@v7 - - name: gh login - run: echo "${{ secrets.SECRET_TOKEN }}" | gh auth login --with-token + - name: Read project metadata and validate authentication + id: project + run: | + if [ -z "$GH_TOKEN" ]; then + echo "::error title=Missing SECRET_TOKEN::Add a GitHub token with Gist and repository Actions variables write access." + exit 1 + fi - - name: Query Model Resolver API and find top download + gh auth status + + python - <<'PY' >> "$GITHUB_OUTPUT" + from pathlib import Path + import tomllib + + path = Path("pyproject.toml") + if not path.is_file(): + raise SystemExit("pyproject.toml was not found") + + with path.open("rb") as file: + config = tomllib.load(file) + + node_id = str(config.get("project", {}).get("name", "")).strip() + if not node_id: + raise SystemExit("[project].name is missing in pyproject.toml") + + print(f"node_id={node_id}") + PY + + - name: Query Comfy Registry API and find top download + env: + COMFY_NODE_ID: ${{ steps.project.outputs.node_id }} run: | max_downloads=0 found_valid_data=false top_node_json="{}" - node_id="Comfyui-Model-Resolver" for i in {1..3}; do echo "Fetching data for attempt $i..." + response_file="registry_response_$i.json" + if ! curl -fsSL --retry 3 --retry-delay 2 --retry-all-errors \ -H "Accept: application/json" \ - "https://api.comfy.org/nodes/${node_id}" > tmp_$i.json; then + "https://api.comfy.org/nodes/${COMFY_NODE_ID}" > "$response_file"; then echo "Error: request failed for attempt $i" - rm -f tmp_$i.json + rm -f "$response_file" continue fi - if [ ! -s tmp_$i.json ] || ! jq empty tmp_$i.json 2>/dev/null; then + if [ ! -s "$response_file" ] || ! jq empty "$response_file" 2>/dev/null; then echo "Error: invalid JSON for attempt $i" - rm -f tmp_$i.json + rm -f "$response_file" continue fi - if jq -e 'type == "array"' tmp_$i.json >/dev/null; then - node_count=$(jq 'length' tmp_$i.json) + if jq -e 'type == "array"' "$response_file" >/dev/null; then + node_count=$(jq 'length' "$response_file") echo "Found $node_count nodes in attempt $i" - for j in $(seq 0 $((node_count - 1))); do - downloads=$(jq -r ".[$j].downloads // 0" tmp_$i.json) - name=$(jq -r ".[$j].name // \"\"" tmp_$i.json) + while IFS= read -r node_json; do + downloads=$(jq -r '.downloads // 0' <<< "$node_json") + name=$(jq -r '.name // ""' <<< "$node_json") if ! [[ "$downloads" =~ ^[0-9]+$ ]]; then downloads=0 @@ -56,13 +100,13 @@ jobs: if [ "$found_valid_data" = false ] || [ "$downloads" -gt "$max_downloads" ]; then found_valid_data=true max_downloads=$downloads - top_node_json=$(jq ".[$j]" tmp_$i.json) + top_node_json=$node_json echo "New maximum found: $downloads (node: $name)" fi - done + done < <(jq -c '.[]' "$response_file") else - downloads=$(jq -r '.downloads // 0' tmp_$i.json) - name=$(jq -r '.name // ""' tmp_$i.json) + downloads=$(jq -r '.downloads // 0' "$response_file") + name=$(jq -r '.name // ""' "$response_file") if ! [[ "$downloads" =~ ^[0-9]+$ ]]; then downloads=0 @@ -71,126 +115,206 @@ jobs: if [ "$found_valid_data" = false ] || [ "$downloads" -gt "$max_downloads" ]; then found_valid_data=true max_downloads=$downloads - top_node_json=$(cat tmp_$i.json) + top_node_json=$(jq -c . "$response_file") echo "New maximum found: $downloads (node: $name)" fi fi - rm -f tmp_$i.json + rm -f "$response_file" done - if [ "$found_valid_data" = true ]; then - echo "$top_node_json" > top_modelresolver.json - echo "Highest download count: $max_downloads" - echo "Node details:" - jq . top_modelresolver.json - else + if [ "$found_valid_data" = false ]; then echo "Error: no valid data found" exit 1 fi - - name: create or update gist with top download - id: set_id - run: | - BADGE_DOC="badges/DOWNLOADS.md" + jq . <<< "$top_node_json" > "$DATA_FILE" + echo "Highest download count: $max_downloads" + jq . "$DATA_FILE" - if gh secret list | grep -q "MODELRESOLVER_GIST_ID" - then - echo "GIST_ID found" - echo "GIST=${{ secrets.MODELRESOLVER_GIST_ID }}" >> $GITHUB_OUTPUT + display_name=$(jq -r '.name // .id // empty' "$DATA_FILE") + publisher_id=$(jq -r '.publisher.id // empty' "$DATA_FILE") + node_slug=$(jq -r '(.id // empty) | ascii_downcase' "$DATA_FILE") - if gh gist view ${{ secrets.MODELRESOLVER_GIST_ID }} &>/dev/null; then - echo "Gist exists and will be updated" - else - echo "Gist does not exist, creating a new one" - gist_id=$(gh gist create top_modelresolver.json | awk -F / '{print $NF}') - echo $gist_id | gh secret set MODELRESOLVER_GIST_ID - echo "GIST=$gist_id" >> $GITHUB_OUTPUT - git rm --ignore-unmatch "$BADGE_DOC" MODELRESOLVER.md - fi - else - echo "Creating a new gist" - gist_id=$(gh gist create top_modelresolver.json | awk -F / '{print $NF}') - echo $gist_id | gh secret set MODELRESOLVER_GIST_ID - echo "GIST=$gist_id" >> $GITHUB_OUTPUT - fi - - - name: create badge if needed - run: | - COUNT=$(jq '.downloads' top_modelresolver.json) - NAME=$(jq -r '.name' top_modelresolver.json) - BADGE_DOC="badges/DOWNLOADS.md" - - if [ ! -f "$BADGE_DOC" ] || ! grep -q "Combined HTML (for-the-badge)" "$BADGE_DOC"; then - mkdir -p badges - shields="https://img.shields.io/badge/dynamic/json?color=informational&label=TopModelResolver&query=downloads&url=" - shields_for_the_badge="https://img.shields.io/badge/dynamic/json?color=2F80ED&label=TopModelResolver&query=downloads&url=" - comfy_badge="https://img.shields.io/badge/ComfyUI-1a1a1a?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAMAAABF0y+mAAAASFBMVEVHcEwYLtsYLtkXLtkXLdkYLtkWLdcFIdoAD95uerfI1XLR3mq3xIP8/yj0/zvw/0FSYMP5/zKMmKQtPNOuuozj8FOhrZW7x4FMWFFbAAAABnRSTlMAUrPX87KxijklAAAA00lEQVR4AX3SBw6DMAxA0UzbrIzO+9+02GkEpoWP9hPZZs06Hw75aI3k4W/+wkQtnGZNhF1I34BzalQcxkmasY0b9raklNcvLYU1GNiiOeVWauOa/XS526gRyzpV/7HeUOG9Jp6vcsvUrCPeKg/3KBKBQhoTD1dQggPWzPVfFOIgo85/kR4y6oB/8SlIEh7wvmTuKd3wgLVW1sTfRBoR7oWVqy/U2NcrWDYMINE7NUuJuoV+2fhaWmnbjzcOWnRv7XbiLh/Y9dNUqk2y0QcNwTu7wgf+/BhsPUhf4QAAAABJRU5ErkJggg==" - combined_downloads="https://img.shields.io/badge/dynamic/json?color=%230D2A4A&label=&query=downloads&url=" - url="https://gist.githubusercontent.com/${{ github.actor }}/${{ steps.set_id.outputs.GIST }}/raw/top_modelresolver.json" - repo="https://registry.comfy.org/publishers/azornes/nodes/comfyui-model-resolver" - echo ''> "$BADGE_DOC" - echo ' - **Markdown** - - ```markdown' >> "$BADGE_DOC" - echo "[![Top Model Resolver Node]($shields$url)]($repo)" >> "$BADGE_DOC" - echo ' - ```' >> "$BADGE_DOC" - echo "[![Top Model Resolver Node]($shields$url)]($repo)" >> "$BADGE_DOC" - echo ' - - **HTML** - ```html' >> "$BADGE_DOC" - echo "Top Model Resolver Node" >> "$BADGE_DOC" - echo ' - ```' >> "$BADGE_DOC" - echo "Top Model Resolver Node" >> "$BADGE_DOC" - echo ' - - **HTML (for-the-badge)** - ```html' >> "$BADGE_DOC" - echo "Top Model Resolver Node" >> "$BADGE_DOC" - echo '```' >> "$BADGE_DOC" - echo "Top Model Resolver Node" >> "$BADGE_DOC" - echo ' - - **Combined Markdown (for-the-badge)** - ```markdown' >> "$BADGE_DOC" - echo "[![ComfyUI]($comfy_badge)![Downloads]($combined_downloads$url&style=for-the-badge)]($repo)" >> "$BADGE_DOC" - echo ' - ```' >> "$BADGE_DOC" - echo "[![ComfyUI]($comfy_badge)![Downloads]($combined_downloads$url&style=for-the-badge)]($repo)" >> "$BADGE_DOC" - echo ' - - **Combined HTML (for-the-badge)** - ```html' >> "$BADGE_DOC" - echo "ComfyUIDownloads" >> "$BADGE_DOC" - echo '```' >> "$BADGE_DOC" - echo "ComfyUIDownloads" >> "$BADGE_DOC" - - git add "$BADGE_DOC" - git config --global user.name "GitHub Action" - git config --global user.email "action@github.com" - git commit -m "Create or update Model Resolver downloads badge" - fi - - - name: Update Gist - run: | - if jq empty top_modelresolver.json 2>/dev/null; then - content=$(jq -c . top_modelresolver.json) - echo "{\"description\": \"Top Model Resolver Node\", \"files\": {\"top_modelresolver.json\": {\"content\": $(jq -Rs . <<< "$content")}}}" > patch.json - - curl -s -X PATCH \ - --user "${{ github.actor }}:${{ secrets.SECRET_TOKEN }}" \ - -H "Content-Type: application/json" \ - -d @patch.json https://api.github.com/gists/${{ steps.set_id.outputs.GIST }} - else - echo "Error: invalid JSON in top_modelresolver.json" + if [ -z "$display_name" ] || [ -z "$publisher_id" ] || [ -z "$node_slug" ]; then + echo "::error title=Incomplete Registry response::The node name, publisher ID, or node ID is missing." exit 1 fi - - name: Push - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} + { + echo "COMFY_NODE_DISPLAY_NAME=$display_name" + echo "COMFY_PUBLISHER_ID=$publisher_id" + echo "COMFY_NODE_SLUG=$node_slug" + } >> "$GITHUB_ENV" + + - name: Create or reuse downloads Gist + id: gist + run: | + gist_id="$COMFY_DOWNLOADS_GIST_ID" + save_gist_id=false + + if [ -z "$gist_id" ]; then + gist_id=$(python - <<'PY' + from pathlib import Path + import os + import re + + pattern = re.compile( + r"query=downloads.*?url=https://gist\.githubusercontent\.com/[^/]+/([0-9a-f]+)", + re.IGNORECASE, + ) + + for filename in (os.environ["BADGE_DOC"], "README.md"): + path = Path(filename) + if not path.is_file(): + continue + match = pattern.search(path.read_text(encoding="utf-8")) + if match: + print(match.group(1)) + break + PY + ) + + if [ -n "$gist_id" ]; then + echo "Found an existing downloads Gist in the badge documentation: $gist_id" + save_gist_id=true + fi + fi + + if [ -n "$gist_id" ] && gh gist view "$gist_id" >/dev/null 2>&1; then + echo "Using existing Gist: $gist_id" + else + if [ -n "$gist_id" ]; then + echo "Configured Gist does not exist or is inaccessible; creating a new one." + else + echo "No Gist is configured; creating one." + fi + + gist_url=$(gh gist create "$DATA_FILE" \ + --desc "ComfyUI downloads for $COMFY_NODE_DISPLAY_NAME") + gist_id="${gist_url##*/}" + save_gist_id=true + fi + + if [ "$save_gist_id" = true ]; then + if ! gh variable set COMFY_DOWNLOADS_GIST_ID --body "$gist_id"; then + echo "::error title=Cannot save Gist ID::Grant SECRET_TOKEN repository Actions variables write access." + exit 1 + fi + + echo "Saved COMFY_DOWNLOADS_GIST_ID=$gist_id" + fi + + gist_filename=$(gh api "gists/$gist_id" --jq ' + .files + | keys + | if index("comfyui_downloads.json") then + "comfyui_downloads.json" + else + (map(select(endswith(".json"))) | first // "comfyui_downloads.json") + end + ') + gist_owner=$(gh api user --jq '.login') + echo "id=$gist_id" >> "$GITHUB_OUTPUT" + echo "filename=$gist_filename" >> "$GITHUB_OUTPUT" + echo "owner=$gist_owner" >> "$GITHUB_OUTPUT" + + - name: Create or update badge document + id: badge + env: + GIST_ID: ${{ steps.gist.outputs.id }} + GIST_FILENAME: ${{ steps.gist.outputs.filename }} + GIST_OWNER: ${{ steps.gist.outputs.owner }} + run: | + mkdir -p "$(dirname "$BADGE_DOC")" + + badge_label=$(jq -rn --arg value "$COMFY_NODE_DISPLAY_NAME" '$value | @uri') + registry_url="https://registry.comfy.org/publishers/${COMFY_PUBLISHER_ID}/nodes/${COMFY_NODE_SLUG}" + gist_url="https://gist.githubusercontent.com/${GIST_OWNER}/${GIST_ID}/raw/${GIST_FILENAME}" + shields="https://img.shields.io/badge/dynamic/json?color=informational&label=${badge_label}&query=downloads&url=" + shields_for_the_badge="https://img.shields.io/badge/dynamic/json?color=2F80ED&label=${badge_label}&query=downloads&url=" + comfy_badge="https://img.shields.io/badge/ComfyUI-1a1a1a?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAMAAABF0y+mAAAASFBMVEVHcEwYLtsYLtkXLtkXLdkYLtkWLdcFIdoAD95uerfI1XLR3mq3xIP8/yj0/zvw/0FSYMP5/zKMmKQtPNOuuozj8FOhrZW7x4FMWFFbAAAABnRSTlMAUrPX87KxijklAAAA00lEQVR4AX3SBw6DMAxA0UzbrIzO+9+02GkEpoWP9hPZZs06Hw75aI3k4W/+wkQtnGZNhF1I34BzalQcxkmasY0b9raklNcvLYU1GNiiOeVWauOa/XS526gRyzpV/7HeUOG9Jp6vcsvUrCPeKg/3KBKBQhoTD1dQggPWzPVfFOIgo85/kR4y6oB/8SlIEh7wvmTuKd3wgLVW1sTfRBoR7oWVqy/U2NcrWDYMINE7NUuJuoV+2fhaWmnbjzcOWnRv7XbiLh/Y9dNUqk2y0QcNwTu7wgf+/BhsPUhf4QAAAABJRU5ErkJggg==" + combined_downloads="https://img.shields.io/badge/dynamic/json?color=%230D2A4A&label=&query=downloads&url=" + + { + echo "**Markdown**" + echo + echo '```markdown' + echo "[![${COMFY_NODE_DISPLAY_NAME}](${shields}${gist_url})](${registry_url})" + echo '```' + echo + echo "[![${COMFY_NODE_DISPLAY_NAME}](${shields}${gist_url})](${registry_url})" + echo + echo "**HTML**" + echo + echo '```html' + echo "${COMFY_NODE_DISPLAY_NAME}" + echo '```' + echo + echo "${COMFY_NODE_DISPLAY_NAME}" + echo + echo "**HTML (for-the-badge)**" + echo + echo '```html' + echo "${COMFY_NODE_DISPLAY_NAME}" + echo '```' + echo + echo "${COMFY_NODE_DISPLAY_NAME}" + echo + echo "**Combined Markdown (for-the-badge)**" + echo + echo '```markdown' + echo "[![ComfyUI](${comfy_badge})![Downloads](${combined_downloads}${gist_url}&style=for-the-badge)](${registry_url})" + echo '```' + echo + echo "[![ComfyUI](${comfy_badge})![Downloads](${combined_downloads}${gist_url}&style=for-the-badge)](${registry_url})" + echo + echo "**Combined HTML (for-the-badge)**" + echo + echo '```html' + echo "ComfyUIDownloads" + echo '```' + echo + echo "ComfyUIDownloads" + } > "$BADGE_DOC" + + if [ -n "$(git status --porcelain -- "$BADGE_DOC")" ]; then + git add "$BADGE_DOC" + git config user.name "GitHub Action" + git config user.email "action@github.com" + git commit -m "Create or update ComfyUI downloads badge" + echo "changed=true" >> "$GITHUB_OUTPUT" + else + echo "Badge document is already up to date." + echo "changed=false" >> "$GITHUB_OUTPUT" + fi + + - name: Update Gist + env: + GIST_ID: ${{ steps.gist.outputs.id }} + GIST_FILENAME: ${{ steps.gist.outputs.filename }} + run: | + if ! jq empty "$DATA_FILE" 2>/dev/null; then + echo "Error: invalid JSON in $DATA_FILE" + exit 1 + fi + + content=$(jq -c . "$DATA_FILE") + jq -n \ + --arg description "ComfyUI downloads for $COMFY_NODE_DISPLAY_NAME" \ + --arg filename "$GIST_FILENAME" \ + --arg content "$content" \ + '{ + description: $description, + files: { + ($filename): { + content: $content + } + } + }' > gist_patch.json + + gh api --method PATCH "gists/${GIST_ID}" --input gist_patch.json >/dev/null + + - name: Push badge document + if: steps.badge.outputs.changed == 'true' + run: git push