Create a template repository

This commit is contained in:
2025-06-05 17:17:44 +02:00
commit 0ac3cdaaa0
24 changed files with 905 additions and 0 deletions

15
.gitattributes vendored Normal file
View File

@@ -0,0 +1,15 @@
*.db filter=lfs diff=lfs merge=lfs -text
*.exe filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.PNG filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.osk filter=lfs diff=lfs merge=lfs -text
*.osr filter=lfs diff=lfs merge=lfs -text
*.osz filter=lfs diff=lfs merge=lfs -text
*.pack filter=lfs diff=lfs merge=lfs -text
*.pdn filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.WAV filter=lfs diff=lfs merge=lfs -text

552
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,552 @@
name: CI/CD Pipeline
on:
push:
branches:
- main
paths:
- '.gitea/workflows/ci.yml'
- 'Skins/**/*'
workflow_dispatch:
env:
DANSER_PATH: "/app/danser/danser-cli"
DANSER_DIR: "/app/danser"
DANSER_VIDEO_DIR: "/app/danser/videos"
DANSER_SCREENSHOT_DIR: "/app/danser/screenshots"
SKINS_DIR: "${{ github.workspace }}/Skins"
REPO_SCREENSHOT_DIR: "${{ github.workspace }}/media/gameplay"
REPO_MOD_ICONS_DIR: "${{ github.workspace }}/media/icons"
REPO_RANKING_PANEL_DIR: "${{ github.workspace }}/media/panel"
SETTINGS_JSON_PATH: "/app/danser/settings/default.json"
README_PATH: "${{ github.workspace }}/README.md"
REPLAY_PATH: "${{ github.workspace }}/src/replay.osr"
OSK_PATH: "${{ github.workspace }}/export"
IMAGE_NAME: arlind/skins
REGISTRY_URL: "https://${{ secrets.CONTAINER_REGISTRY }}"
jobs:
generate_everything:
name: Full CI/CD Pipeline
runs-on: ubuntu-latest
container:
image: ${{ secrets.CONTAINER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest
options: >-
--gpus all
--privileged
--security-opt seccomp=unconfined
--security-opt apparmor=unconfined
--cap-add=ALL
--env NVIDIA_DRIVER_CAPABILITIES=all
--env NVIDIA_VISIBLE_DEVICES=all
--user 0:0
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
token: ${{ secrets.TOKEN }}
- name: Git LFS Pull
run: |
echo "Pulling Git LFS files..."
git lfs pull
echo "LFS files pulled."
- name: Set XDG_RUNTIME_DIR
run: |
echo "Setting XDG_RUNTIME_DIR..."
mkdir -p /tmp/xdg_runtime_dir
chmod 0700 /tmp/xdg_runtime_dir
echo "XDG_RUNTIME_DIR=/tmp/xdg_runtime_dir" >> "$GITHUB_ENV"
echo "XDG_RUNTIME_DIR set."
- name: Create directories for assets
run: |
echo "Creating asset directories..."
mkdir -p "$REPO_SCREENSHOT_DIR"
mkdir -p "$REPO_MOD_ICONS_DIR"
mkdir -p "$REPO_RANKING_PANEL_DIR"
mkdir -p "$OSK_PATH"
echo "Asset directories created successfully."
- name: Create New Tag
run: |
echo "Computing new tag..."
git fetch --tags >/dev/null 2>&1
latest_tag=$(git describe --tags $(git rev-list --tags --max-count=1) 2>/dev/null || echo "")
if [ -z "$latest_tag" ]; then
new_tag="v1.0.0"
else
version=${latest_tag#v}
major=$(echo "$version" | cut -d. -f1)
minor=$(echo "$version" | cut -d. -f2)
patch=$(echo "$version" | cut -d. -f3)
minor=$((minor + 1))
patch=0
new_tag="v${major}.${minor}.${patch}"
fi
echo "new_tag=$new_tag" >> $GITHUB_ENV
echo "Computed new tag: $new_tag"
- name: Generate Danser videos and screenshots
run: |
echo "[Danser Job Started]"
SKIN_COUNT=$(find "$SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
INDEX=1
for skin in "$SKINS_DIR"/*/; do
if [ -d "$skin" ]; then
SKIN_NAME=$(basename "$skin")
echo ""
echo "[$INDEX/$SKIN_COUNT] Skin: $SKIN_NAME"
LOGFILE="/tmp/danser_log_$SKIN_NAME.txt"
FFMPEG_LOG="/tmp/ffmpeg_log_$SKIN_NAME.txt"
echo " → Generating video..."
if ! xvfb-run -a "$DANSER_DIR/danser-cli" -replay "$REPLAY_PATH" -record -skip -start=215 -end=230 -noupdatecheck -out="$SKIN_NAME" -skin "$SKIN_NAME" >"$LOGFILE" 2>&1; then
echo " ✖ Video generation failed for $SKIN_NAME. Log output:"
cat "$LOGFILE"
exit 1
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 generation failed for $SKIN_NAME. Log output:"
cat "$LOGFILE"
exit 1
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
echo " ✖ FFmpeg conversion failed for $SKIN_NAME. Log output:"
cat "$FFMPEG_LOG"
exit 1
fi
mv "$DANSER_VIDEO_DIR/$SKIN_NAME.gif" "$REPO_SCREENSHOT_DIR/$SKIN_NAME.gif"
fi
if [ -f "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" ]; then
mv "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" "$REPO_RANKING_PANEL_DIR/$SKIN_NAME.png"
fi
echo " ✓ Completed"
INDEX=$((INDEX + 1))
fi
done
echo ""
echo "[Danser Job Finished — $SKIN_COUNT skins processed]"
- name: Rename Generated Assets Based on skin.ini
run: |
echo "[Asset Renaming Started]"
INDEX=1
SKIN_COUNT=$(find "$SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
for skin_path in "$SKINS_DIR"/*/; do
if [ -d "$skin_path" ]; then
SKIN_NAME=$(basename "$skin_path")
echo ""
echo "[$INDEX/$SKIN_COUNT] Skin: $SKIN_NAME"
ini_file=$(find "$skin_path" -maxdepth 1 -iname "skin.ini" | head -n1)
skin_header="$SKIN_NAME"
if [ -f "$ini_file" ]; then
name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n1)
if [ -n "$name_line" ]; then
new_name=$(echo "$name_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[ -n "$new_name" ] && skin_header="$new_name"
fi
fi
original_gif="$REPO_SCREENSHOT_DIR/$SKIN_NAME.gif"
renamed_gif="$REPO_SCREENSHOT_DIR/$skin_header.gif"
if [ -f "$original_gif" ] && [ "$original_gif" != "$renamed_gif" ]; then
mv -f "$original_gif" "$renamed_gif"
echo " ✓ Renamed GIF"
else
echo " → No GIF to rename or already named correctly"
fi
original_png="$REPO_RANKING_PANEL_DIR/$SKIN_NAME.png"
renamed_png="$REPO_RANKING_PANEL_DIR/$skin_header.png"
if [ -f "$original_png" ] && [ "$original_png" != "$renamed_png" ]; then
mv -f "$original_png" "$renamed_png"
echo " ✓ Renamed PNG"
else
echo " → No PNG to rename or already named correctly"
fi
echo " ✓ Completed"
INDEX=$((INDEX + 1))
fi
done
echo ""
echo "[Asset Renaming Complete — $SKIN_COUNT skins processed]"
- name: Generate Mod Icons
run: |
echo "[Mod Icon Generation Started]"
ICONS_JSON_FILE="${{ github.workspace }}/.gitea/workflows/icons.json"
group1_icons=$(jq -r '.group1 | join(" ")' "$ICONS_JSON_FILE")
group2_icons=$(jq -r '.group2 | join(" ")' "$ICONS_JSON_FILE")
group3_icons=$(jq -r '.group3 | join(" ")' "$ICONS_JSON_FILE")
SKIN_COUNT=$(find "$SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
INDEX=1
for skin_path in "$SKINS_DIR"/*/; do
if [ -d "$skin_path" ]; then
SKIN_NAME=$(basename "$skin_path")
echo ""
echo "[$INDEX/$SKIN_COUNT] Skin: $SKIN_NAME"
ini_file=$(find "$skin_path" -maxdepth 1 -iname "skin.ini" | head -n1)
skin_header="$SKIN_NAME"
if [ -f "$ini_file" ]; then
name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n1)
if [ -n "$name_line" ]; then
new_name=$(echo "$name_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$new_name" ]; then
skin_header="$new_name"
fi
fi
fi
ICON_FOLDER="$skin_path"
OUTPUT="${REPO_MOD_ICONS_DIR}/${skin_header}-mod-icons.png"
TILE_SIZE=160
PADDING=10
MAX_ICONS=7
BLANK_IMAGE="blank.png"
magick -size "${TILE_SIZE}x${TILE_SIZE}" xc:none "$BLANK_IMAGE"
row_images=""
row_index=1
for group_list in "$group1_icons" "$group2_icons" "$group3_icons"; do
montage_files=""
count=0
for icon in $group_list; do
icon_path="${ICON_FOLDER}/selection-mod-${icon}@2x.png"
if [ -f "$icon_path" ]; then
montage_files="$montage_files \"$icon_path\""
count=$((count + 1))
elif [ -f "$SKINS_DIR/Default/selection-mod-${icon}@2x.png" ]; then
montage_files="$montage_files \"$SKINS_DIR/Default/selection-mod-${icon}@2x.png\""
count=$((count + 1))
fi
done
missing=$(( MAX_ICONS - count ))
if [ "$missing" -lt 0 ]; then
missing=0
fi
i=0
while [ "$i" -lt "$missing" ]; do
montage_files="$montage_files \"$BLANK_IMAGE\""
i=$((i + 1))
done
row_file="row_${row_index}.png"
eval "magick montage $montage_files -tile \"${MAX_ICONS}x1\" -geometry \"${TILE_SIZE}x${TILE_SIZE}+${PADDING}+${PADDING}\" -background none \"$row_file\""
row_images="$row_images \"$row_file\""
row_index=$((row_index + 1))
done
num_rows=0
for _ in $row_images; do
num_rows=$((num_rows + 1))
done
eval "magick montage $row_images -tile \"1x${num_rows}\" -geometry \"+${PADDING}+${PADDING}\" -background none \"$OUTPUT\""
rm "$BLANK_IMAGE"
rm row_*.png
echo " ✓ Completed"
INDEX=$((INDEX + 1))
fi
done
echo ""
echo "[Mod Icon Generation Finished — $SKIN_COUNT skins processed]"
- name: Create OSK files
run: |
echo "[OSK Creation Job Started]"
SKIN_COUNT=$(find "$SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
INDEX=1
FIXED_TIMESTAMP="2025-01-01 00:00:00"
find "$SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skin; do
SKIN_FOLDER=$(basename "$skin")
echo ""
echo "[$INDEX/$SKIN_COUNT] Processing skin folder: $SKIN_FOLDER"
ini_file=$(find "$skin" -maxdepth 1 -iname "skin.ini" | head -n1)
if [ -f "$ini_file" ]; then
name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n1)
if [ -n "$name_line" ]; then
new_name=$(echo "$name_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$new_name" ]; then
SKIN_FOLDER="$new_name"
fi
fi
else
echo " → No skin.ini found, using folder name."
fi
osk_file="${OSK_PATH}/${SKIN_FOLDER}.osk"
if ! (cd "$skin" && find . -type f -exec touch -d "$FIXED_TIMESTAMP" {} +); then
echo " ✖ Failed to normalize timestamps in $skin"
exit 1
fi
if (
cd "$skin" && \
find . -type f | sort | zip -rq -D -X -9 --compression-method deflate "$osk_file" -@
); then
echo " ✓ OSK file created successfully."
else
echo " ✖ Failed to create OSK file: $osk_file"
exit 1
fi
INDEX=$((INDEX + 1))
done
echo ""
echo "[OSK Creation Job Finished — $SKIN_COUNT skins processed]"
- name: Generate README
run: |
echo "Starting README generation..."
SKINS_JSON_FILE="${{ github.workspace }}/.gitea/workflows/skins.json"
DESC_FILE=$(mktemp)
jq -r '.descriptions | to_entries[] | "\(.key)=\(.value)"' "$SKINS_JSON_FILE" > "$DESC_FILE"
echo "---" > "$README_PATH"
echo "gitea: none" >> "$README_PATH"
echo "include_toc: true" >> "$README_PATH"
echo "---" >> "$README_PATH"
echo "" >> "$README_PATH"
echo "# Skins" >> "$README_PATH"
echo "" >> "$README_PATH"
echo "This README is automatically generated using CI/CD pipelines, Danser, ImageMagick, and FFmpeg. The workflow generates gameplay previews, creates mod icons, packages .osk files, and updates the README with media and descriptions, with the Skins included in [Skins](./Skins)." >> "$README_PATH"
echo "" >> "$README_PATH"
get_desc() {
key=$1
escaped_key=$(printf '%s\n' "$key" | sed 's/[\/&]/\\&/g')
grep "^${escaped_key}=" "$DESC_FILE" | cut -d '=' -f2-
}
ORDER_FILE=$(mktemp)
JSON_SKINS_TMP=$(mktemp)
SEEN_HEADERS_FILE=$(mktemp)
jq -r '.order[]' "$SKINS_JSON_FILE" > "$ORDER_FILE"
cp "$ORDER_FILE" "$JSON_SKINS_TMP"
while IFS= read -r skin; do
dir="$SKINS_DIR/$skin"
if [ ! -d "$dir" ]; then
echo "Skipping missing skin directory: $skin"
continue
fi
ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n 1)
skin_header="$skin"
if [ -f "$ini_file" ]; then
name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n 1)
if [ -n "$name_line" ]; then
skin_header=$(echo "$name_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
fi
fi
if grep -Fxq "$skin_header" "$SEEN_HEADERS_FILE"; then
echo "Skipping duplicate skin header from JSON order: $skin_header"
continue
fi
echo "$skin_header" >> "$SEEN_HEADERS_FILE"
escaped_img=$(echo "$skin_header.gif" | sed 's/ /%20/g')
escaped_osk=$(echo "$skin_header.osk" | sed 's/ /%20/g')
echo "## [$skin_header]($REGISTRY_URL/arlind/skins/media/tag/$new_tag/export/$escaped_osk)" >> "$README_PATH"
echo "" >> "$README_PATH"
skin_desc=$(get_desc "$skin")
if [ -n "$skin_desc" ]; then
echo "$skin_desc" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
if [ -f "$ini_file" ]; then
author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n 1 || true)
if [ -n "$author_line" ]; then
author=$(echo "$author_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$author" ]; then
echo "**Author:** $author" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
fi
fi
echo "![$skin_header Gameplay](media/gameplay/$escaped_img)" >> "$README_PATH"
echo "" >> "$README_PATH"
if [ -f "media/panel/${skin_header}.png" ]; then
escaped_panel=$(echo "${skin_header}.png" | sed 's/ /%20/g')
echo "![$skin_header Ranking Panel](media/panel/$escaped_panel)" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
mod_icon_file="${skin_header}-mod-icons.png"
if [ -f "media/icons/$mod_icon_file" ]; then
escaped_mod=$(echo "$mod_icon_file" | sed 's/ /%20/g')
echo "![$skin_header Mods](media/icons/$escaped_mod)" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
done < "$ORDER_FILE"
find "$SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | while IFS= read -r dir; do
skin=$(basename "$dir")
if [ "$skin" = "Default" ]; then
continue
fi
ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n 1)
skin_header="$skin"
if [ -f "$ini_file" ]; then
name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n 1)
if [ -n "$name_line" ]; then
skin_header=$(echo "$name_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
fi
fi
if grep -Fxq "$skin_header" "$SEEN_HEADERS_FILE"; then
continue
fi
if grep -Fxq "$skin" "$JSON_SKINS_TMP"; then
continue
fi
echo "$skin_header" >> "$SEEN_HEADERS_FILE"
escaped_img=$(echo "$skin_header.gif" | sed 's/ /%20/g')
escaped_osk=$(echo "$skin_header.osk" | sed 's/ /%20/g')
echo "## [$skin_header]($REGISTRY_URL/arlind/skins/media/tag/$new_tag/export/$escaped_osk)" >> "$README_PATH"
echo "" >> "$README_PATH"
if [ -f "$ini_file" ]; then
author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n 1 || true)
if [ -n "$author_line" ]; then
author=$(echo "$author_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$author" ]; then
echo "**Author:** $author" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
fi
fi
echo "![$skin_header Gameplay](media/gameplay/$escaped_img)" >> "$README_PATH"
echo "" >> "$README_PATH"
if [ -f "media/panel/${skin_header}.png" ]; then
escaped_panel=$(echo "${skin_header}.png" | sed 's/ /%20/g')
echo "![$skin_header Ranking Panel](media/panel/$escaped_panel)" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
mod_icon_file="${skin_header}-mod-icons.png"
if [ -f "media/icons/$mod_icon_file" ]; then
escaped_mod=$(echo "$mod_icon_file" | sed 's/ /%20/g')
echo "![$skin_header Mods](media/icons/$escaped_mod)" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
done
rm "$DESC_FILE" "$ORDER_FILE" "$JSON_SKINS_TMP" "$SEEN_HEADERS_FILE"
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)\`](https://git.sulejmani.xyz/arlind/skins/src/tag/$new_tag/README.md) | $current_commit_date |" >> "$README_PATH"
git tag --sort=-v:refname | grep -v "^$new_tag$" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | 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\`](https://git.sulejmani.xyz/arlind/skins/src/tag/$tag/README.md) | $formatted_date |" >> "$README_PATH"
done
echo "README generation completed."
- name: Configure Git
run: |
echo "Configuring git user and LFS..."
git config user.email "arlind@sulej.ch"
git config user.name "ci-bot"
git config lfs.https://${{ secrets.CONTAINER_REGISTRY }}/arlind/skins.git/info/lfs.locksverify true
echo "Git configured."
- name: Remove Default skin
run: |
echo "Removing Default skin assets if they exist..."
rm -f "$REPO_SCREENSHOT_DIR/Default.gif"
rm -f "$REPO_MOD_ICONS_DIR/Default-mod-icons.png"
rm -f "$REPO_RANKING_PANEL_DIR/Default.png"
rm -f "$OSK_PATH/Default.osk"
echo "Default skin assets removed."
- name: Add and Commit changes
run: |
echo "Staging files for commit..."
git add README.md media/gameplay/* media/panel/* media/icons/* export/*
echo "Committing changes..."
git commit -m "[ci skip] push back from pipeline" -q || echo "No changes to commit"
echo "Commit step completed."
- name: Push changes and create tag
run: |
echo "Checking branch and pushing changes..."
if [ "${GITHUB_REF}" = "refs/heads/main" ]; then
echo "On main branch: pushing to origin main..."
git push origin HEAD:main || echo "No changes to push"
echo "Creating and pushing tag $new_tag..."
git tag "$new_tag"
git push origin "$new_tag"
else
echo "On branch ${GITHUB_REF_NAME}: pushing to origin ${GITHUB_REF_NAME}..."
git push origin HEAD:"${GITHUB_REF_NAME}" || echo "No changes to push"
fi
echo "Push step completed."

View File

@@ -0,0 +1,25 @@
{
"group1": [
"easy",
"nofail",
"halftime"
],
"group2": [
"hardrock",
"suddendeath",
"perfect",
"doubletime",
"nightcore",
"hidden",
"flashlight"
],
"group3": [
"relax",
"relax2",
"autoplay",
"target",
"spunout",
"cinema",
"scorev2"
]
}

View File

@@ -0,0 +1,10 @@
{
"order": [
"example1",
"example2"
],
"descriptions": {
"example1": "Description of example1",
"example2": "Description of example2"
}
}

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
*did*
*WhiteCat*
*Niven*
*oreru*
*Night*
*aiupscale*
*test*
*Teto*
*Tesotra*

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Arlind-dev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

16
hardlink-songs-folder.bat Normal file
View File

@@ -0,0 +1,16 @@
@echo off
net session >nul 2>&1
if %errorLevel% neq 0 (
echo Requesting admin privileges...
powershell -Command "Start-Process cmd -ArgumentList '/c \"%~f0\"' -Verb RunAs"
exit /b
)
echo Running robocopy sync as Administrator...
echo.
robocopy "E:\osu!\skins" "D:\git\skins\Skins" /MIR /COPYALL /SEC /B /XJ /DCOPY:T /J
echo.
echo Robocopy sync completed.
pause

206
how-to-use.md Normal file
View File

@@ -0,0 +1,206 @@
---
gitea: none
include_toc: true
---
# How to Use This Repository
If anything is unclear, just shoot me a message on Discord.
## 1. Create Your Account on git.sulejmani.xyz
Sign up here:
[https://git.sulejmani.xyz/user/sign\_up](https://git.sulejmani.xyz/user/sign_up)
![Register](/src/docs/register.png)
Check your email and click the confirmation link to activate your account.
![Check Mail](/src/docs/check_mail.png)
![Activation Email](/src/docs/activation_mail.png)
Then confirm your password to complete the process.
![Activate Account](/src/docs/activate_account.png)
---
## 2. Generate an Access Token
Go to [https://git.sulejmani.xyz/](https://git.sulejmani.xyz/) and open your **Settings**.
![Navigate Settings](/src/docs/navigate_settings.png)
Under the **Applications** section, create a new token.
* Check all permissions (as shown in the image)
* Give it a clear and recognizable name
![Generate Token](/src/docs/applications_generate_token.png)
Once the token is generated, **copy and save it securely**, you wont be able to see it again.
![Copy Token](/src/docs/copy_token.png)
---
## 3. Create a New Repository
Still on [https://git.sulejmani.xyz/](https://git.sulejmani.xyz/), click the **+** icon in the top right corner to create a new repo.
![Start Creating New Repo](/src/docs/start_create_new_repo.png)
* Name it however you like
* Use `skins-template` as the template
* Enable all options as shown
You now have your own copy of the template repo.
![Template Repo](/src/docs/template_repo.png)
---
## 4. Set Up Secrets for Deployment
Go to your repository's **Settings**.
![Navigate Repo Settings](/src/docs/navigate_repository_settings.png)
Then open the **Secrets** tab and add these two:
| Secret Name | Secret Value |
| -------------------- | -------------------------- |
| `TOKEN` | The token you just created |
| `CONTAINER_REGISTRY` | `git.sulejmani.xyz` |
![Add Secret 1](/src/docs/secret1.png)
![Add Secret 2](/src/docs/secret2.png)
---
## 5. Clone the Repository
If you dont have Git installed, download it here:
👉 [https://git-scm.com/downloads/win](https://git-scm.com/downloads/win)
Choose where to clone the repo (e.g. `D:/git/`) and run:
```bash
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git lfs install
git clone https://git.sulejmani.xyz/{yourusername}/{yourrepositoryname}.git
```
---
## 6. Prepare Your Skins
In your cloned repo, open `hardlink-songs-folder.bat`.
Edit the source and target paths. For example, if:
* Your osu! skins are in `E:\osu!\skins`
* Your repo is in `D:\git\skins`
Then update the line on the script like this:
```bat
robocopy "E:\osu!\skins" "D:\git\skins\Skins" /MIR /COPYALL /SEC /B /XJ /DCOPY:T /J
```
Run the batch file by double-clicking it.
---
## 7. Change Skin Order, add descriptions, and ignore Skins.
### Skin Order
This is optional, but recommended. In the `workflows` folder, open `skins.json`.
Edit the order and descriptions as you like.
here's an example:
```json
{
"order": [
"- Barely still Jace",
"- Jace"
],
"descriptions": {
"- Barely still Jace": "My main skin.",
"- Jace": "My secondary skin, only really used for EZ."
}
}
```
Make sure the order matches the skins folder name in the `Skins` folder.
### Ignore Skins
There is a .gitignore file in the root of the repo. You can add any skins you don't want to be uploaded.
Here's an example:
```
*WhiteCat*
*Vaxei*
```
So in this example if a Skin has WhiteCat or Vaxei in the name, it will be ignored.
---
## 8. Upload Your Skins
Open a terminal (cmd or PowerShell) in your repo directory:
```cmd
cd D:\git\skins # update this to your actual path
```
Then run:
```bash
git add .
git commit -m "Adding skins"
git config --global credential.helper cache
git push
```
Your skins will now be uploaded.
---
## 9. Monitor Upload Progress
Go to your repo's **Actions** tab.
![navigate-actions](/src/docs/navigate_actions.png)
Youll see the workflow progress there.
![progress](/src/docs/progress.png)
If something goes wrong, check the logs—or just ping me on Discord.
---
## 10. Update Skins in the Future
To update your skins later:
1. Re-run `hardlink-songs-folder.bat`
2. Run the same upload commands:
```bash
git pull
git add .
git commit -m "Updating skins"
git push
```

6
rsync.sh Normal file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
rsync -av --delete /mnt/e/osu\!/Skins/ /home/nixos/git/skins/Skins/
find ./Skins/ -type f ! -perm 644 -exec chmod 644 {} +
find ./Skins/ -type d ! -perm 755 -exec chmod 755 {} +

BIN
src/docs/activate_account.png LFS Normal file

Binary file not shown.

BIN
src/docs/activation_mail.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
src/docs/check_mail.png LFS Normal file

Binary file not shown.

BIN
src/docs/copy_token.png LFS Normal file

Binary file not shown.

BIN
src/docs/navigate_actions.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
src/docs/navigate_settings.png LFS Normal file

Binary file not shown.

BIN
src/docs/progress.png LFS Normal file

Binary file not shown.

BIN
src/docs/register.png LFS Normal file

Binary file not shown.

BIN
src/docs/secret1.png LFS Normal file

Binary file not shown.

BIN
src/docs/secret2.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
src/docs/template_repo.png LFS Normal file

Binary file not shown.

BIN
src/replay.osr LFS Normal file

Binary file not shown.