diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index eea7014..d048413 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -56,29 +56,13 @@ jobs: tags: true token: ${{ secrets.TOKEN }} - - name: Discover all skins - shell: bash - run: | - echo "Discovering all skins in $SKINS_DIR…" - mapfile -t skins < <( - find "$SKINS_DIR" -mindepth 1 -maxdepth 1 -type d \ - | sed 's|'"$SKINS_DIR"'/||' - ) - { - echo 'ALL_SKINS_DIR<> "$GITHUB_ENV" - echo "→ ALL_SKINS_DIR set (newline-delimited list)" - - name: Detect Changed Skin Directories shell: bash run: | echo "[Detect Changed Skin Directories Started]" - readarray -t all_skins <<< "$ALL_SKINS_DIR" + echo "→ Fetching tags from git..." + git fetch --tags force_rebuild="${{ github.event.inputs.force_rebuild }}" skins=() @@ -87,9 +71,12 @@ jobs: echo "→ Force rebuild flag: $force_rebuild" if [[ "$force_rebuild" == "true" ]]; then - echo "→ Force rebuild is enabled. Using ALL_SKINS_DIR for full list…" - skins=("${all_skins[@]}") - echo " ✓ Found ${#skins[@]} skin directories (from ALL_SKINS_DIR)" + echo "→ Force rebuild is enabled. Finding all skin directories..." + mapfile -t skins < <( + find Skins -mindepth 1 -maxdepth 1 -type d \ + | sed 's|^Skins/||' + ) + echo " ✓ Found ${#skins[@]} skin directories" else echo "→ Force rebuild is disabled. Finding latest git tag..." @@ -97,21 +84,25 @@ jobs: if [[ -n "$latest_tag" ]]; then echo "→ Latest tag found: $latest_tag" - echo "→ Finding added/modified skins since $latest_tag…" + echo "→ Finding added/modified skins since $latest_tag..." mapfile -t skins < <( git diff --name-only -z --diff-filter=AM "$latest_tag" HEAD \ | while IFS= read -r -d '' file; do - [[ $file == Skins/* ]] && echo "${file#Skins/}" | cut -d/ -f1 + if [[ $file == Skins/* ]]; then + echo "${file#Skins/}" | cut -d/ -f1 + fi done ) echo " ✓ Found ${#skins[@]} added/modified skins" - echo "→ Finding deleted skins since $latest_tag…" + echo "→ Finding deleted skins since $latest_tag..." mapfile -t deleted_skins < <( git diff --name-only -z --diff-filter=D "$latest_tag" HEAD \ | while IFS= read -r -d '' file; do - [[ $file == Skins/* ]] && echo "${file#Skins/}" | cut -d/ -f1 + if [[ $file == Skins/* ]]; then + echo "${file#Skins/}" | cut -d/ -f1 + fi done ) if [ "${#deleted_skins[@]}" -gt 0 ]; then @@ -123,9 +114,12 @@ jobs: fi else - echo "→ No tag found. Falling back to ALL_SKINS_DIR for full list…" - skins=("${all_skins[@]}") - echo " ✓ Found ${#skins[@]} skin directories (from ALL_SKINS_DIR)" + echo "→ No tag found. Falling back to finding all skin directories..." + mapfile -t skins < <( + find Skins -mindepth 1 -maxdepth 1 -type d \ + | sed 's|^Skins/||' + ) + echo " ✓ Found ${#skins[@]} skin directories" fi fi @@ -135,7 +129,9 @@ jobs: for skin in "${skins[@]}"; do skin="${skin#"${skin%%[![:space:]]*}"}" skin="${skin%"${skin##*[![:space:]]}"}" - [[ -n "$skin" ]] && uniq_skins+=("$skin") + if [ -n "$skin" ]; then + uniq_skins+=("$skin") + fi done echo " ✓ ${#uniq_skins[@]} valid skin names after cleaning" @@ -200,22 +196,9 @@ jobs: - name: Create directories for assets shell: bash run: | - echo "Creating base directories for assets..." + echo "Creating directories for assets..." mkdir -p "$REPO_SCREENSHOT_DIR" "$REPO_MOD_ICONS_DIR" "$REPO_RANKING_PANEL_DIR" "$OSK_PATH" - - # Read the newline-delimited list back into an array - readarray -t skins <<< "$ALL_SKINS_DIR" - - for skin in "${skins[@]}"; do - echo " → Creating subdirs for '$skin'…" - mkdir -p \ - "$REPO_SCREENSHOT_DIR/$skin" \ - "$REPO_MOD_ICONS_DIR/$skin" \ - "$REPO_RANKING_PANEL_DIR/$skin" \ - "$OSK_PATH/$skin" - done - - echo "All asset directories created for ${#skins[@]} skins." + echo "Directories created." - name: Create New Tag shell: bash @@ -325,66 +308,77 @@ jobs: fi mapfile -t skins < "$CHANGED_SKINS_FILE" - [ "${#skins[@]}" -eq 0 ] && { echo "No skins to process. Exiting."; exit 0; } + + if [ "${#skins[@]}" -eq 0 ]; then + echo "No skins changed after reading file. Skipping generation." + exit 0 + fi SKIN_COUNT=${#skins[@]} INDEX=1 for skin_path in "${skins[@]}"; do [ -z "$skin_path" ] && continue - SKIN_DIR="$DANSER_SKINS_DIR/$skin_path" - [ ! -d "$SKIN_DIR" ] && { echo "Skipping missing skin: $skin_path"; continue; } - SKIN_NAME="$skin_path" - OUT_GIF_DIR="$REPO_SCREENSHOT_DIR/$SKIN_NAME" - OUT_PNG_DIR="$REPO_RANKING_PANEL_DIR/$SKIN_NAME" + SKIN_DIR="$DANSER_SKINS_DIR/$skin_path" + if [ ! -d "$SKIN_DIR" ]; then + echo "Skipping missing skin directory: $SKIN_DIR" + continue + fi + + SKIN_NAME=$(echo "$skin_path" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') echo "" - echo "[$INDEX/$SKIN_COUNT] Generating for skin: $SKIN_NAME" + echo "[$INDEX/$SKIN_COUNT] Skin: $SKIN_NAME" LOGFILE="/tmp/danser_log_$INDEX.txt" FFMPEG_LOG="/tmp/ffmpeg_log_$INDEX.txt" echo " → Generating video..." if ! xvfb-run -a "$DANSER_DIR/danser-cli" \ - -replay "$REPLAY_PATH" -record -skip -start=215 -end=230 -noupdatecheck \ + -replay "$REPLAY_PATH" \ + -record -skip -start=215 -end=230 -noupdatecheck \ -out="$SKIN_NAME" -skin="$SKIN_NAME" >"$LOGFILE" 2>&1; then - echo " ✖ Video failed for $SKIN_NAME"; cat "$LOGFILE"; INDEX=$((INDEX+1)); continue + echo " ✖ Video generation failed for $SKIN_NAME. Log output:" + cat "$LOGFILE" + continue fi echo " → Taking screenshot..." if ! xvfb-run -a "$DANSER_DIR/danser-cli" \ -replay "$REPLAY_PATH" -skip -noupdatecheck -ss 243 \ -out="$SKIN_NAME" -skin="$SKIN_NAME" >>"$LOGFILE" 2>&1; then - echo " ✖ Screenshot failed for $SKIN_NAME"; cat "$LOGFILE"; INDEX=$((INDEX+1)); continue + echo " ✖ Screenshot generation failed for $SKIN_NAME. Log output:" + cat "$LOGFILE" + continue fi if [ -f "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4" ]; then echo " → Converting to GIF..." - if ffmpeg -y -hwaccel cuda -ss 4 -t 10 -i "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4" \ - -filter_complex "[0:v] fps=24,scale=720:-1:flags=lanczos,palettegen [p]; [0:v] fps=24,scale=720:-1:flags=lanczos [x]; [x][p] paletteuse" \ - -c:v gif "$DANSER_VIDEO_DIR/$SKIN_NAME.gif" >"$FFMPEG_LOG" 2>&1; then - mv "$DANSER_VIDEO_DIR/$SKIN_NAME.gif" "$OUT_GIF_DIR/$SKIN_NAME.gif" - echo " ✓ GIF moved to $OUT_GIF_DIR/" - else - echo " ✖ FFmpeg conversion failed for $SKIN_NAME"; cat "$FFMPEG_LOG" + if ! ffmpeg -y -hwaccel cuda -ss 4 -t 10 -i "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4" \ + -filter_complex "[0:v] fps=24,scale=720:-1:flags=lanczos,palettegen [p]; [0:v] fps=24,scale=720:-1:flags=lanczos [x]; [x][p] paletteuse" \ + -c:v gif "$DANSER_VIDEO_DIR/$SKIN_NAME.gif" >"$FFMPEG_LOG" 2>&1; then + echo " ✖ FFmpeg conversion failed for $SKIN_NAME. Log output:" + cat "$FFMPEG_LOG" + continue fi + mv "$DANSER_VIDEO_DIR/$SKIN_NAME.gif" "$REPO_SCREENSHOT_DIR/$SKIN_NAME.gif" else - echo " ✖ No MP4 found for $SKIN_NAME" + echo " ✖ Video file not found for $SKIN_NAME." fi if [ -f "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" ]; then - mv "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" "$OUT_PNG_DIR/$SKIN_NAME.png" - echo " ✓ PNG moved to $OUT_PNG_DIR/" + mv "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" "$REPO_RANKING_PANEL_DIR/$SKIN_NAME.png" else - echo " ✖ No PNG found for $SKIN_NAME" + echo " ✖ Screenshot file not found for $SKIN_NAME." fi + echo " ✓ Completed" INDEX=$((INDEX + 1)) done echo "" - echo "[Danser Job Finished — processed $SKIN_COUNT skins]" + echo "[Danser Job Finished — $SKIN_COUNT skins processed]" - name: Rename Generated Assets Based on skin.ini shell: bash @@ -397,7 +391,10 @@ jobs: fi mapfile -t skins < "$CHANGED_SKINS_FILE" - [ "${#skins[@]}" -eq 0 ] && { echo "No skins to rename. Exiting."; exit 0; } + if [ "${#skins[@]}" -eq 0 ]; then + echo "No skins changed after reading file. Skipping asset renaming." + exit 0 + fi SKIN_COUNT=${#skins[@]} INDEX=1 @@ -411,47 +408,41 @@ jobs: for skin_path in "${skins[@]}"; do [ -z "$skin_path" ] && continue - SKIN_DIR_NAME="$skin_path" SKIN_DIR="$DANSER_SKINS_DIR/$skin_path" - if [ ! -d "$SKIN_DIR" ]; then - echo "Skipping missing skin directory: $SKIN_DIR" - continue - fi + [ ! -d "$SKIN_DIR" ] && { echo "Skipping missing skin directory: $SKIN_DIR"; continue; } - echo "Processing skin $INDEX/$SKIN_COUNT: $SKIN_DIR_NAME" + SKIN_NAME=$(basename -- "$skin_path" | tr -d '\r\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + echo "Processing skin $INDEX/$SKIN_COUNT: $SKIN_NAME" - skin_header="$SKIN_DIR_NAME" ini_file=$(find "$SKIN_DIR" -maxdepth 1 -iname "skin.ini" | head -n1 || true) + skin_header="$SKIN_NAME" if [ -f "$ini_file" ]; then - name_line=$(grep -i '^[[:space:]]*Name:' "$ini_file" | head -n1 || true) + name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n1 || true) if [ -n "$name_line" ]; then - val="${name_line#*:}" - val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" - sanitized="$(sanitize_filename "$val")" - [ -n "$sanitized" ] && skin_header="$sanitized" + new_name=$(echo "$name_line" | cut -d':' -f2- \ + | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + new_name=$(sanitize_filename "$new_name") + [ -n "$new_name" ] && skin_header="$new_name" fi fi - GIF_DIR="$REPO_SCREENSHOT_DIR/$SKIN_DIR_NAME" - PNG_DIR="$REPO_RANKING_PANEL_DIR/$SKIN_DIR_NAME" - - if [ -f "$GIF_DIR/$SKIN_DIR_NAME.gif" ] && [ "$SKIN_DIR_NAME" != "$skin_header" ]; then - mv -f "$GIF_DIR/$SKIN_DIR_NAME.gif" \ - "$GIF_DIR/$skin_header.gif" || true - echo " ✓ Renamed GIF to $GIF_DIR/$skin_header.gif" + if [ -f "$REPO_SCREENSHOT_DIR/$SKIN_NAME.gif" ] && [ "$SKIN_NAME" != "$skin_header" ]; then + mv -f "$REPO_SCREENSHOT_DIR/$SKIN_NAME.gif" \ + "$REPO_SCREENSHOT_DIR/$skin_header.gif" || true + echo " ✓ Renamed GIF" fi - if [ -f "$PNG_DIR/$SKIN_DIR_NAME.png" ] && [ "$SKIN_DIR_NAME" != "$skin_header" ]; then - mv -f "$PNG_DIR/$SKIN_DIR_NAME.png" \ - "$PNG_DIR/$skin_header.png" || true - echo " ✓ Renamed PNG to $PNG_DIR/$skin_header.png" + if [ -f "$REPO_RANKING_PANEL_DIR/$SKIN_NAME.png" ] && [ "$SKIN_NAME" != "$skin_header" ]; then + mv -f "$REPO_RANKING_PANEL_DIR/$SKIN_NAME.png" \ + "$REPO_RANKING_PANEL_DIR/$skin_header.png" || true + echo " ✓ Renamed PNG" fi INDEX=$((INDEX + 1)) done echo "" - echo "[Asset Renaming Complete — processed $SKIN_COUNT skins]" + echo "[Asset Renaming Complete — $SKIN_COUNT skins processed]" - name: Generate Mod Icons shell: bash @@ -464,7 +455,10 @@ jobs: fi mapfile -t skin_dirs < "$CHANGED_SKINS_FILE" - [ "${#skin_dirs[@]}" -eq 0 ] && { echo "No skins to process. Exiting."; exit 0; } + if [ "${#skin_dirs[@]}" -eq 0 ]; then + echo "No skins changed after reading file. Skipping mod icon generation." + exit 0 + fi sanitize_filename() { echo "$1" \ @@ -486,17 +480,17 @@ jobs: for skin_path in "${skin_dirs[@]}"; do SKIN_DIR="$DANSER_SKINS_DIR/$skin_path" - [ ! -d "$SKIN_DIR" ] && { echo "Skipping missing skin directory: $SKIN_DIR"; ((INDEX++)); continue; } + [ ! -d "$SKIN_DIR" ] && { echo "Skipping missing skin directory: $SKIN_DIR"; continue; } + skin_header=$(basename -- "$skin_path" | tr -d '\r\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') - skin_header="$skin_path" ini_file=$(find "$SKIN_DIR" -maxdepth 1 -iname "skin.ini" | head -n1 || true) if [ -f "$ini_file" ]; then - name_line=$(grep -i '^[[:space:]]*Name:' "$ini_file" | head -n1 || true) + name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n1 || true) if [ -n "$name_line" ]; then - val="${name_line#*:}" - val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" - sanitized="$(sanitize_filename "$val")" - [ -n "$sanitized" ] && skin_header="$sanitized" + new_name=$(echo "$name_line" | cut -d ':' -f2- \ + | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + new_name=$(sanitize_filename "$new_name") + [ -n "$new_name" ] && skin_header="$new_name" fi fi @@ -504,9 +498,7 @@ jobs: echo "[$INDEX/$SKIN_COUNT] Skin: $skin_header" ICON_FOLDER="$SKIN_DIR" - OUTPUT_DIR="$REPO_MOD_ICONS_DIR/$skin_path" - mkdir -p "$OUTPUT_DIR" - OUTPUT="$OUTPUT_DIR/${skin_header}-mod-icons.png" + OUTPUT="${REPO_MOD_ICONS_DIR}/${skin_header}-mod-icons.png" row_images=() row_index=1 @@ -523,26 +515,22 @@ jobs: while [ "${#montage_files[@]}" -lt 7 ]; do montage_files+=("$BLANK_IMAGE") done - magick montage "${montage_files[@]}" \ - -tile "7x1" -geometry "160x160+10+10" -background none \ - "row_${row_index}.png" + magick montage "${montage_files[@]}" -tile "7x1" -geometry "160x160+10+10" -background none "row_${row_index}.png" row_images+=("row_${row_index}.png") row_index=$((row_index + 1)) done - magick montage "${row_images[@]}" \ - -tile "1x${#row_images[@]}" -geometry "+10+10" -background none \ - "$OUTPUT" + magick montage "${row_images[@]}" -tile "1x${#row_images[@]}" -geometry "+10+10" -background none "$OUTPUT" rm row_*.png - echo " ✓ Mod Icons Generated at $OUTPUT" + echo " ✓ Mod Icons Generated" INDEX=$((INDEX + 1)) done rm "$BLANK_IMAGE" echo "" - echo "[Mod Icon Generation Finished — processed $SKIN_COUNT skins]" + echo "[Mod Icon Generation Finished — $SKIN_COUNT skins processed]" - name: Create OSK Files shell: bash @@ -555,7 +543,10 @@ jobs: fi mapfile -t skin_dirs < "$CHANGED_SKINS_FILE" - [ "${#skin_dirs[@]}" -eq 0 ] && { echo "No skins to process. Exiting."; exit 0; } + if [ "${#skin_dirs[@]}" -eq 0 ]; then + echo "No skins changed after reading file. Skipping OSK creation." + exit 0 + fi sanitize_filename() { echo "$1" \ @@ -570,20 +561,17 @@ jobs: for skin_path in "${skin_dirs[@]}"; do SKIN_DIR="$DANSER_SKINS_DIR/$skin_path" - [ ! -d "$SKIN_DIR" ] && { echo "Skipping missing skin directory: $SKIN_DIR"; ((INDEX++)); continue; } + [ ! -d "$SKIN_DIR" ] && { echo "Skipping missing skin directory: $SKIN_DIR"; continue; } - OUTPUT_DIR="$OSK_PATH/$skin_path" - mkdir -p "$OUTPUT_DIR" - - skin_header="$skin_path" + skin_header=$(basename -- "$skin_path" | tr -d '\r\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') ini_file=$(find "$SKIN_DIR" -maxdepth 1 -iname "skin.ini" | head -n1 || true) if [ -f "$ini_file" ]; then name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n1 || true) if [ -n "$name_line" ]; then - val="${name_line#*:}" - val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" - sanitized="$(sanitize_filename "$val")" - [ -n "$sanitized" ] && skin_header="$sanitized" + new_name=$(echo "$name_line" | cut -d ':' -f2- \ + | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + new_name=$(sanitize_filename "$new_name") + [ -n "$new_name" ] && skin_header="$new_name" fi fi @@ -591,17 +579,14 @@ jobs: echo "[$INDEX/$SKIN_COUNT] Processing skin: $skin_header" (cd "$SKIN_DIR" && find . -type f -exec touch -d "$FIXED_TIMESTAMP" {} +) + (cd "$SKIN_DIR" && find . -type f | sort | zip -rq -D -X -9 --compression-method deflate "$OSK_PATH/${skin_header}.osk" -@) - (cd "$SKIN_DIR" && find . -type f | sort | \ - zip -rq -D -X -9 --compression-method deflate \ - "$OUTPUT_DIR/${skin_header}.osk" -@) - - echo " ✓ OSK file created at $OUTPUT_DIR/${skin_header}.osk" + echo " ✓ OSK file created successfully." INDEX=$((INDEX + 1)) done echo "" - echo "[OSK Creation Job Finished — processed $SKIN_COUNT skins]" + echo "[OSK Creation Job Finished — $SKIN_COUNT skins processed]" - name: Generate README shell: bash @@ -618,7 +603,7 @@ jobs: SKINS_JSON_FILE="${{ github.workspace }}/.gitea/workflows/skins.json" DESC_FILE=$(mktemp) - echo "Step 1: Extracting descriptions from skins.json..." + echo "Step 1: Extracting descriptions..." jq -r '.descriptions | to_entries[] | "\(.key)=\(.value)"' "$SKINS_JSON_FILE" > "$DESC_FILE" echo "Step 2: Starting to build README..." @@ -633,260 +618,180 @@ jobs: echo "osuid: $OSU_ID" >> "$README_PATH" echo "-->" >> "$README_PATH" echo "" >> "$README_PATH" - echo "**Go back to [osc/skins]($REGISTRY_URL/$USER_REPOSITORY/osc/skins)**" >> "$README_PATH" + echo "**Go back to [osc/skins](https://git.sulejmani.xyz/osc/skins)**" >> "$README_PATH" echo "" >> "$README_PATH" - get_desc() { - grep -F -m1 -- "$1=" "$DESC_FILE" 2>/dev/null | cut -d '=' -f2- - } + get_desc() { grep -- "^$1=" "$DESC_FILE" 2>/dev/null | cut -d '=' -f2-; } ORDER_FILE=$(mktemp) - echo "Step 3: Extracting order from skins.json..." - jq -r '.order[]?' "$SKINS_JSON_FILE" > "$ORDER_FILE" + echo "Step 3: Reading order..." + jq -r '.order[]' "$SKINS_JSON_FILE" > "$ORDER_FILE" - echo "Step 4: Processing ordered skins..." + echo "Step 4: Rendering ordered skins..." while IFS= read -r skin; do - echo " Processing skin (order): $skin" dir="$DANSER_SKINS_DIR/$skin" - [ ! -d "$dir" ] && { echo " Skipping missing directory: $dir"; continue; } + [ ! -d "$dir" ] && continue - ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n1) - skin_header="$skin" - if [ -f "$ini_file" ]; then - line=$(grep -i '^[[:space:]]*Name:' "$ini_file" | head -n1 2>/dev/null || true) - [ -n "$line" ] && { - val="${line#*:}" - skin_header=$(sanitize_filename "$val") - } + ini="$dir/skin.ini" + header="$skin" + if [ -f "$ini" ]; then + name_line=$(grep -i '^[[:space:]]*Name:' "$ini" | head -n1) + if [ -n "$name_line" ]; then + val="${name_line#*:}" + header="$(sanitize_filename "${val#"${val%%[![:space:]]*}"}")" + fi fi - base_path=$(printf "%s/%s" "$skin" "$skin_header" | sed 's/ /%20/g') - echo "## [$skin_header]($REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/${base_path}.osk)" >> "$README_PATH" + base="$(printf "%s/%s" "$skin" "$header" | sed 's/ /%20/g')" + img="${base}.gif" + panel="${base}.png" + osk="${base}.osk" + mod="${base}-mod-icons.png" + + echo "## [$header]($REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/$osk)" >> "$README_PATH" echo "" >> "$README_PATH" desc=$(get_desc "$skin") [ -n "$desc" ] && { echo "$desc" >> "$README_PATH"; echo "" >> "$README_PATH"; } - if [ -f "$ini_file" ]; then - author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n1 2>/dev/null || true) + if [ -f "$ini" ]; then + author_line=$(grep -i '^[[:space:]]*Author:' "$ini" | head -n1) if [ -n "$author_line" ]; then - author="${author_line#*:}" - author="$(echo "$author" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" - [ -n "$author" ] && { echo "**Author:** $author" >> "$README_PATH"; echo "" >> "$README_PATH"; } + auth="${author_line#*:}" + auth="${auth#"${auth%%[![:space:]]*}"}" + [ -n "$auth" ] && { echo "**Author:** $auth" >> "$README_PATH"; echo "" >> "$README_PATH"; } fi fi - echo "![$skin_header Gameplay](media/gameplay/${base_path}.gif)" >> "$README_PATH" + echo "![$header Gameplay](media/gameplay/$img)" >> "$README_PATH" echo "" >> "$README_PATH" - echo "![$skin_header Ranking Panel](media/panel/${base_path}.png)" >> "$README_PATH" + echo "![$header Ranking Panel](media/panel/$panel)" >> "$README_PATH" echo "" >> "$README_PATH" - echo "![$skin_header Mods](media/icons/${base_path}-mod-icons.png)" >> "$README_PATH" + echo "![$header Mods](media/icons/$mod)" >> "$README_PATH" echo "" >> "$README_PATH" done < "$ORDER_FILE" - declare -A ordered - while IFS= read -r skin; do - ordered["$skin"]=1 - done < "$ORDER_FILE" + echo "Step 5: Rendering extra skins..." + find "$DANSER_SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | while IFS= read -r dir; do + skin=$(basename "$dir") + grep -qx "$skin" "$ORDER_FILE" && continue - echo "Step 5: Processing extra skins..." - if [ ! -s "$ORDER_FILE" ]; then - echo "→ No \`order\` defined in skins.json; skipping extra-skins loop." - else - for dir in "$DANSER_SKINS_DIR"/*; do - [ -d "$dir" ] || continue - skin=$(basename "$dir") - [[ -n "${ordered[$skin]}" ]] && continue - - echo " Writing extra skin: $skin" - ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n1) - skin_header="$skin" - if [ -f "$ini_file" ]; then - line=$(grep -i '^[[:space:]]*Name:' "$ini_file" | head -n1 2>/dev/null || true) - [ -n "$line" ] && { - val="${line#*:}" - skin_header=$(sanitize_filename "$val") - } + ini="$dir/skin.ini" + header="$skin" + if [ -f "$ini" ]; then + name_line=$(grep -i '^[[:space:]]*Name:' "$ini" | head -n1) + if [ -n "$name_line" ]; then + val="${name_line#*:}" + header="$(sanitize_filename "${val#"${val%%[![:space:]]*}"}")" fi + fi - base_path=$(printf "%s/%s" "$skin" "$skin_header" | sed 's/ /%20/g') - echo "## [$skin_header]($REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/${base_path}.osk)" >> "$README_PATH" - echo "" >> "$README_PATH" + base="$(printf "%s/%s" "$skin" "$header" | sed 's/ /%20/g')" + img="${base}.gif" + panel="${base}.png" + osk="${base}.osk" + mod="${base}-mod-icons.png" - if [ -f "$ini_file" ]; then - author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n1 2>/dev/null || true) - if [ -n "$author_line" ]; then - author="${author_line#*:}" - author="$(echo "$author" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" - [ -n "$author" ] && { echo "**Author:** $author" >> "$README_PATH"; echo "" >> "$README_PATH"; } - fi + echo "## [$header]($REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/$osk)" >> "$README_PATH" + echo "" >> "$README_PATH" + + if [ -f "$ini" ]; then + author_line=$(grep -i '^[[:space:]]*Author:' "$ini" | head -n1) + if [ -n "$author_line" ]; then + auth="${author_line#*:}" + auth="${auth#"${auth%%[![:space:]]*}"}" + [ -n "$auth" ] && { echo "**Author:** $auth" >> "$README_PATH"; echo "" >> "$README_PATH"; } fi + fi - echo "![$skin_header Gameplay](media/gameplay/${base_path}.gif)" >> "$README_PATH" - echo "" >> "$README_PATH" - echo "![$skin_header Ranking Panel](media/panel/${base_path}.png)" >> "$README_PATH" - echo "" >> "$README_PATH" - echo "![$skin_header Mods](media/icons/${base_path}-mod-icons.png)" >> "$README_PATH" - echo "" >> "$README_PATH" - done - fi + echo "![$header Gameplay](media/gameplay/$img)" >> "$README_PATH" + echo "" >> "$README_PATH" + echo "![$header Ranking Panel](media/panel/$panel)" >> "$README_PATH" + echo "" >> "$README_PATH" + echo "![$header Mods](media/icons/$mod)" >> "$README_PATH" + echo "" >> "$README_PATH" + done - echo "Step 7: Writing Build History section..." + echo "Step 6: Build History..." echo "# Build History" >> "$README_PATH" echo "" >> "$README_PATH" echo "| Version | Date |" >> "$README_PATH" echo "| ------- | ---- |" >> "$README_PATH" - - current_commit_date=$(TZ="Europe/Zurich" date -d "$(git log -1 --format=%cI)" "+%d.%m.%Y %H:%M:%S") - echo "| [\`$new_tag (Current)\`]($REGISTRY_URL/$USER_REPOSITORY/src/tag/$new_tag/README.md) | $current_commit_date |" >> "$README_PATH" - - old_tags=$(git tag --sort=-v:refname | grep -v "^$new_tag$" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' || true) - if [ -n "$old_tags" ]; then - echo "$old_tags" | while read -r tag; do - tag_date=$(git log -1 --format=%ci "$tag") - formatted_date=$(TZ="Europe/Zurich" date -d "$tag_date" "+%d.%m.%Y %H:%M:%S") - echo "| [\`$tag\`]($REGISTRY_URL/$USER_REPOSITORY/src/tag/$tag/README.md) | $formatted_date |" >> "$README_PATH" - done - fi + commit_date=$(TZ="Europe/Zurich" date -d "$(git log -1 --format=%cI)" "+%d.%m.%Y %H:%M:%S") + echo "| [\`$new_tag (Current)\`](https://git.sulejmani.xyz/arlind/skins/src/tag/$new_tag/README.md) | $commit_date |" >> "$README_PATH" + for tag in $(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | grep -v "^$new_tag$"); do + date=$(git log -1 --format=%ci "$tag") + fmt=$(TZ="Europe/Zurich" date -d "$date" "+%d.%m.%Y %H:%M:%S") + echo "| [\`$tag\`](https://git.sulejmani.xyz/arlind/skins/src/tag/$tag/README.md) | $fmt |" >> "$README_PATH" + done echo "README generation completed successfully." - - name: Migrate flat assets into per-skin folders - shell: bash - run: | - set -euo pipefail - echo "[Migration of flat assets started]" - - sanitize() { - echo "$1" | tr -d $'\r\n\t' | \ - sed -e 's#[\\/:\*\?"<>|]#-#g' -e 's#%#_#g' | \ - tr -s ' ' | \ - sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' - } - - readarray -t skins <<< "$ALL_SKINS_DIR" - declare -A hdr2fld - for skin in "${skins[@]}"; do - [[ -z "${skin// }" ]] && continue - dir_key=$(sanitize "$skin") - hdr2fld["$dir_key"]="$skin" - ini=$(find "$DANSER_SKINS_DIR/$skin" -maxdepth 1 -iname skin.ini -print -quit || true) - if [[ -f "$ini" ]]; then - raw=$(grep -i '^\s*Name:' "$ini" | head -n1 || true) - raw="${raw#*:}" - ini_key=$(sanitize "$raw") - [[ -n "$ini_key" ]] && hdr2fld["$ini_key"]="$skin" - fi - done - - echo "DEBUG: header→folder map" - for key in "${!hdr2fld[@]}"; do - echo " '$key' → '${hdr2fld[$key]}'" - done - - migrate() { - local root=$1 ext=$2 - echo " → Migrating *.$ext in $root" - set +e - find "$root" -maxdepth 2 -type f -name "*.$ext" | while read -r f; do - filename=$(basename "$f") - base="${filename%.*}" - [[ "$root" == *icons ]] && base="${base%-mod-icons}" - key=$(sanitize "$base") - target="${hdr2fld[$key]:-}" - if [[ -n "$target" ]]; then - dst="$root/$target" - mkdir -p "$dst" - srcdir=$(dirname "$f") - if [[ "$srcdir" == "$dst" ]]; then - echo " → $filename already in $target/, skipping" - else - echo " • Moving $filename → $target/" - mv "$f" "$dst/$filename" - fi - else - echo " ✖ No mapping for '$base' → leaving $f" - fi - done - set -euo pipefail - - find "$root" -mindepth 1 -maxdepth 1 -type d | while read -r d; do - [ -z "$(ls -A "$d")" ] && { echo " • Removing empty dir $d"; rmdir "$d"; } - done - } - - migrate "$REPO_SCREENSHOT_DIR" gif - migrate "$REPO_RANKING_PANEL_DIR" png - migrate "$REPO_MOD_ICONS_DIR" png - migrate "$OSK_PATH" osk - - echo "[Migration of flat assets complete]" - - name: Cleanup Extra Files shell: bash run: | set -euo pipefail + echo "[Cleanup Extra Files Started]" - # Remove legacy docs - [ -d src/docs ] && rm -rf src/docs - [ -f how-to-use.md ] && rm -f how-to-use.md + rm -rf src/docs || true + rm -f how-to-use.md || true - # Read the current list of skins - readarray -t skins <<< "$ALL_SKINS_DIR" - - # Helper to sanitize header (for pruning inside) - sanitize() { + sanitize_filename() { echo "$1" \ - | tr -d '\r\n' \ | sed -e 's#[\\/:\*\?"<>|]#-#g' -e 's#%#_#g' \ | tr -s ' ' \ - | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' + | sed 's/^ *//;s/ *$//' } - for root in "$REPO_SCREENSHOT_DIR" "$REPO_RANKING_PANEL_DIR" "$REPO_MOD_ICONS_DIR" "$OSK_PATH"; do - for dir in "$root"/*; do - [ -d "$dir" ] || continue - name="$(basename "$dir")" - if ! printf '%s\n' "${skins[@]}" | grep -Fxq -- "$name"; then - echo " → Skin '$name' deleted—removing directory $dir" - rm -rf "$dir" + expected_basenames=() + for dir in "$DANSER_SKINS_DIR"/*; do + [ -d "$dir" ] || continue + + raw=$(basename "$dir" | tr -d '\r\n') + header=$(sanitize_filename "$raw") + expected_basenames+=("$header") + + if ini=$(find "$dir" -maxdepth 1 -iname skin.ini | head -n1); then + if name_line=$(grep -i '^[[:space:]]*name:' "$ini" | head -n1); then + val="${name_line#*:}" + val=$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + header=$(sanitize_filename "$val") + expected_basenames+=("$header") fi - done + fi done - for skin in "${skins[@]}"; do - header=$(sanitize "$skin") - ini=$(find "$DANSER_SKINS_DIR/$skin" -maxdepth 1 -type f -iname "skin.ini" -print -quit || true) - if [[ -f "$ini" ]]; then - raw=$(grep -i '^[[:space:]]*Name:' "$ini" | head -n1 || true) - raw="${raw#*:}" - header=$(sanitize "$raw") - fi + readarray -t expected_basenames < <( + printf "%s\n" "${expected_basenames[@]}" | sort -u + ) + for b in "${expected_basenames[@]}"; do + expected_basenames+=("${b}-mod-icons") + done + readarray -t expected_basenames < <( + printf "%s\n" "${expected_basenames[@]}" | sort -u + ) - expect_gif="$header.gif" - expect_png="$header.png" - expect_icon="$header-mod-icons.png" - expect_osk="$header.osk" - - prune_dir() { - root=$1; expected=$2 - dir="$root/$skin" - [ ! -d "$dir" ] && return - for f in "$dir"/*; do - [ -e "$f" ] || continue - if [[ "$(basename "$f")" != "$expected" ]]; then - echo " → Removing unexpected file: $f" - rm -f "$f" + prune_dir() { + for f in "$1"/*; do + [ -e "$f" ] || continue + base="${f##*/}" + base="${base%.*}" + keep=false + for kb in "${expected_basenames[@]}"; do + if [[ "$base" == "$kb" ]]; then + keep=true + break fi done - } + $keep || rm -rf "$f" + done + } - prune_dir "$REPO_SCREENSHOT_DIR" "$expect_gif" - prune_dir "$REPO_RANKING_PANEL_DIR" "$expect_png" - prune_dir "$REPO_MOD_ICONS_DIR" "$expect_icon" - prune_dir "$OSK_PATH" "$expect_osk" - done + prune_dir "$REPO_SCREENSHOT_DIR" + prune_dir "$REPO_RANKING_PANEL_DIR" + prune_dir "$REPO_MOD_ICONS_DIR" + prune_dir "$OSK_PATH" echo "[Cleanup Extra Files Complete]" @@ -895,6 +800,7 @@ jobs: run: | git config user.email "arlind@sulej.ch" git config user.name "ci-bot" + git config lfs.https://${{ vars.CONTAINER_REGISTRY }}/arlind/skins.git/info/lfs.locksverify true - name: Add and Commit changes shell: bash