name: Update Community Skins README on: schedule: - cron: '*/5 * * * *' workflow_dispatch: env: README_PATH: "${{ github.workspace }}/README.md" IMAGE_NAME: osc/skins-image GITEA_API: https://${{ vars.CONTAINER_REGISTRY }}/api/v1 ARTIFACT_PATH: "/data" USER_ROWS_FILE: "/data/user_rows.txt" AVATAR_ROWS_FILE: "/data/avatar_rows.txt" jobs: gather-skins: runs-on: ubuntu-latest container: image: ${{ vars.CONTAINER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest steps: - id: mask-secrets name: Mask Sensitive Tokens run: | echo "::add-mask::${{ secrets.TOKEN }}" echo "::add-mask::${{ secrets.OSUAPIV1 }}" - id: checkout-code name: Checkout Repository uses: actions/checkout@v4 with: token: ${{ secrets.TOKEN }} - id: fetch-skins-data name: Find Skin Repositories and Generate Tables run: | set -eo pipefail mkdir -p "$ARTIFACT_PATH" total_valid_entries=0 json_valid() { jq -e . >/dev/null 2>&1 } curl_json() { body=$(mktemp) code=$(curl --retry 3 --retry-delay 5 -s -o "$body" -w "%{http_code}" "$1") printf "%s\n%s" "$code" "$body" } page=1 while :; do # Fetch user list read users_code users_body < <(curl_json "$GITEA_API/admin/users?limit=50&page=$page&_ts=$(date +%s)") users_json=$(cat "$users_body") if [ "$users_code" != "200" ] || ! printf "%s" "$users_json" | json_valid; then echo "❌ Invalid users JSON on page $page (HTTP $users_code)" echo "$users_json" > "$ARTIFACT_PATH/invalid_users_page_${page}.json" break fi count=$(printf "%s" "$users_json" | jq 'length') [ "$count" -eq 0 ] && break echo "📄 Found $count users on page $page" for i in $(seq 0 $((count - 1))); do user_login=$(printf "%s" "$users_json" | jq -r ".[$i].login") echo "🔍 User: $user_login" # Fetch repos read repos_code repos_body < <(curl_json "$GITEA_API/users/$user_login/repos?_ts=$(date +%s)") repos_json=$(cat "$repos_body") if [ "$repos_code" != "200" ] || ! printf "%s" "$repos_json" | json_valid; then echo " ❌ Invalid repo list for $user_login (HTTP $repos_code)" echo "$repos_json" > "$ARTIFACT_PATH/invalid_repos_${user_login}.json" continue fi repos_count=$(printf "%s" "$repos_json" | jq 'length') echo " 📦 $repos_count repos found" [ "$repos_count" -eq 0 ] && continue for j in $(seq 0 $((repos_count - 1))); do owner=$(printf "%s" "$repos_json" | jq -r ".[$j].owner.login") repo=$(printf "%s" "$repos_json" | jq -r ".[$j].name") html_url=$(printf "%s" "$repos_json" | jq -r ".[$j].html_url") echo " → Repo: $owner/$repo" # Check Skins directory read dir_code dir_body < <(curl_json "$GITEA_API/repos/$owner/$repo/contents/Skins?_ts=$(date +%s)") if [ "$dir_code" != "200" ]; then echo " ❌ Skipped: No Skins/ directory (HTTP $dir_code)" continue fi # Check README.md read readme_code readme_body < <(curl_json "$GITEA_API/repos/$owner/$repo/contents/README.md?_ts=$(date +%s)") readme_json=$(cat "$readme_body") if [ "$readme_code" != "200" ] || ! printf "%s" "$readme_json" | json_valid; then echo " ❌ Skipped: No README.md or invalid JSON (HTTP $readme_code)" continue fi content=$(printf "%s" "$readme_json" | jq -r .content | base64 -d 2>/dev/null || echo "") osu_id=$(printf "%s" "$content" | grep -oE "osuid:[ ]*[0-9]+" | grep -oE "[0-9]+" | head -1) if [ -z "$osu_id" ]; then echo " ❌ Skipped: No osuid in README" continue fi # Fetch osu API read api_code api_body < <(curl_json "https://osu.ppy.sh/api/get_user?k=${{ secrets.OSUAPIV1 }}&u=$osu_id&type=id&_ts=$(date +%s)") osu_json=$(cat "$api_body") if ! printf "%s" "$osu_json" | json_valid; then echo " ❌ Invalid osu! API JSON for $osu_id" printf "%s" "$osu_json" > "$ARTIFACT_PATH/invalid_osu_user_${osu_id}.json" continue fi if [ "$(printf "%s" "$osu_json" | jq 'length')" -eq 0 ]; then echo " 🚫 Restricted or banned user" username="$owner" pp_rank="RESTRICTED" pp_country_rank="RESTRICTED" padded_rank="9999999" profile_url="https://osu.ppy.sh/users/$osu_id" timestamp=$(( $(date +%s)/86400*86400 )) avatar_url="https://a.ppy.sh/$osu_id?$timestamp" printf "%s|%s%s%sProfileSkins\n" \ "$padded_rank" "$username" "$pp_rank" "$pp_country_rank" "$profile_url" "$html_url" >> "$USER_ROWS_FILE" printf "%s|\n" \ "$padded_rank" "$html_url" "$avatar_url" >> "$AVATAR_ROWS_FILE" total_valid_entries=$((total_valid_entries + 1)) continue fi # Normal user pp_rank=$(printf "%s" "$osu_json" | jq -r '.[0].pp_rank // "9999999"') pp_country_rank=$(printf "%s" "$osu_json" | jq -r '.[0].pp_country_rank // "-"') country=$(printf "%s" "$osu_json" | jq -r '.[0].country // "-"') username=$(printf "%s" "$osu_json" | jq -r '.[0].username // "'$owner'"') padded_rank=$(printf "%07d" "$pp_rank") if [ "$pp_country_rank" != "-" ] && [ "$country" != "-" ]; then cc_rank="${country}${pp_country_rank}" else cc_rank="-" fi printf "%s|%s%s%sProfileSkins\n" \ "$padded_rank" "$username" "$pp_rank" "$cc_rank" "$osu_id" "$html_url" >> "$USER_ROWS_FILE" timestamp=$(( $(date +%s)/86400*86400 )) printf "%s|\n" \ "$padded_rank" "$html_url" "$osu_id" "$timestamp" >> "$AVATAR_ROWS_FILE" echo " ✅ Added: $username (#$pp_rank, $cc_rank)" total_valid_entries=$((total_valid_entries + 1)) done done page=$((page + 1)) done echo "✅ Total valid entries found: $total_valid_entries" - id: upload-tables name: Upload artifacts uses: actions/upload-artifact@v3 with: name: skin-tables path: ${{ env.ARTIFACT_PATH }} retention-days: 1 generate-readme: needs: gather-skins runs-on: ubuntu-latest container: image: ${{ vars.CONTAINER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest outputs: readme_changed: ${{ steps.check-readme.outputs.changed }} steps: - id: download-skin-data name: Download skin data uses: actions/download-artifact@v3 with: name: skin-tables path: ${{ env.ARTIFACT_PATH }} - id: checkout-code name: Checkout Repository uses: actions/checkout@v4 with: token: ${{ secrets.TOKEN }} - id: generate-readme name: Generate README run: | set -eo pipefail cat > "$README_PATH" <<-EOF # osu! Swiss Community Skin collection Welcome to the osu! Swiss Community Skin collection, this repository archives and showcases Skins osc members use. Enjoy looking around, click file names to download the skins and click on the images to see more about the skins. ## How do I add my skins here? If you're interested in adding your skins here please follow this tutorial [how-to-use](/how-to-use.md) ## Skins
list instead of icons
EOF sort -t '|' -k1,1n "$USER_ROWS_FILE" | cut -d'|' -f2- | sed 's/^/ /' >> "$README_PATH" cat >> "$README_PATH" <<-EOF
Name Global Rank Country Rank Profile Skins

EOF sort -t '|' -k1,1n "$AVATAR_ROWS_FILE" | cut -d'|' -f2- | sed 's/^/ /' >> "$README_PATH" cat >> "$README_PATH" <<-EOF

EOF - id: check-readme name: Check for README changes run: | set -euo pipefail # Make sure the workspace is considered safe by git inside containers git config --global --add safe.directory "$GITHUB_WORKSPACE" # Ensure diff works even if file were new (intent-to-add) git add -N "$README_PATH" || true if git diff --quiet -- "$README_PATH"; then echo "✅ README unchanged" echo "changed=false" >> "$GITHUB_OUTPUT" else echo "⚠️ README was modified" echo "changed=true" >> "$GITHUB_OUTPUT" git --no-pager diff --stat -- "$README_PATH" || true fi - id: upload-updated-readme name: Upload updated README if: steps.check-readme.outputs.changed == 'true' uses: actions/upload-artifact@v3 with: name: updated-readme path: ${{ env.README_PATH }} retention-days: 1 commit-readme: needs: generate-readme if: needs.generate-readme.outputs.readme_changed == 'true' runs-on: ubuntu-latest container: image: ${{ vars.CONTAINER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest steps: - id: checkout-code name: Checkout Repository uses: actions/checkout@v4 with: token: ${{ secrets.TOKEN }} - id: configure-git name: Configure Git 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 - id: rebase-repository name: Rebase Repository run: | git fetch origin main git rebase origin/main || { echo "⚠️ Git rebase failed, likely due to conflicts." git rebase --abort || true exit 1 } - id: download-updated-readme name: Download README uses: actions/download-artifact@v3 with: name: updated-readme path: . - id: commit-and-push name: Commit and Push README run: | git config advice.addIgnoredFile false git add README.md git commit -m "[ci skip] push back from pipeline" -q || echo "No changes to commit" if [ "${GITHUB_REF}" = "refs/heads/main" ]; then git push origin HEAD:main || echo "No changes to push" else git push origin HEAD:"${GITHUB_REF_NAME}" || echo "No changes to push" fi