New app icon, packaged with a menu entry for Linux
Build / test (pull_request) Successful in 22s
Build / build (pull_request) Skipped

The icon is a plate wearing headphones with a red Nordic house cookie
on it. It replaces the default Godot icon, so the game window, Windows
and macOS builds use it.

Linux programs can't carry their own icon, so the build now also makes
MusicPlayer_<version>_linux.tar.gz with the game, the icon as a PNG, a
.desktop menu entry and install.sh, which installs them for the current
user. The bare Linux binary is still published.
This commit is contained in:
Sansan committed 2026-10-06 08:32:56 +02:00
1 parent 29b11560b5
commit 10e9e7b2a4
10 files changed
+239 -6

No files matched your search

+5
View File
@@ -83,6 +83,11 @@ jobs:
godot --headless --path . --import godot --headless --path . --import
godot --headless --path . --export-release "Windows Desktop" "build/${PROJECT_NAME}_${GAME_VERSION}.exe" godot --headless --path . --export-release "Windows Desktop" "build/${PROJECT_NAME}_${GAME_VERSION}.exe"
godot --headless --path . --export-release "Linux" "build/${PROJECT_NAME}_${GAME_VERSION}.x86_64" godot --headless --path . --export-release "Linux" "build/${PROJECT_NAME}_${GAME_VERSION}.x86_64"
# Linux binaries can't hold an icon; the package adds it with a menu entry and installer
- name: Package Linux
run: |
linux/package.sh "build/${PROJECT_NAME}_${GAME_VERSION}.x86_64" "$GAME_VERSION" build
ls -la build ls -la build
- name: Upload Artifacts - name: Upload Artifacts
+10 -1
View File
@@ -11,10 +11,19 @@ same version in the About window.
The `Build` workflow (`.gitea/workflows/gitea-build.yaml`) then: The `Build` workflow (`.gitea/workflows/gitea-build.yaml`) then:
1. runs the tests, 1. runs the tests,
2. exports the Windows and Linux builds, 2. exports the Windows and Linux builds, and packages Linux as
`MusicPlayer_<version>_linux.tar.gz` with the icon, an app menu entry and `install.sh`
(Linux programs can't carry their own icon the way a Windows .exe does),
3. if no tag exists for the version yet: uploads the builds to the package registry 3. if no tag exists for the version yet: uploads the builds to the package registry
under `MusicPlayer/<version>/`, and creates the `<version>` tag and Gitea release under `MusicPlayer/<version>/`, and creates the `<version>` tag and Gitea release
with the builds attached. with the builds attached.
Pushes that don't change the version, and pushes to `develop`, only test and export. Pushes that don't change the version, and pushes to `develop`, only test and export.
Don't create tags or `1.x.y` branches by hand; the workflow makes the tag. Don't create tags or `1.x.y` branches by hand; the workflow makes the tag.
## Installing on Linux
Unpack `MusicPlayer_<version>_linux.tar.gz` and run `./install.sh` inside the
`MusicPlayer` folder. It installs the game to `~/.local/bin` and adds it, with its icon,
to the app menu. The bare `MusicPlayer_<version>.x86_64` is still published for anyone
who just wants to run the file.
+4 -4
View File
@@ -8,7 +8,7 @@ dedicated_server=false
custom_features="" custom_features=""
export_filter="all_resources" export_filter="all_resources"
include_filter="" include_filter=""
exclude_filter="tests/*, addons/gdUnit4/*" exclude_filter="tests/*, addons/gdUnit4/*, linux/*"
export_path="../../MusicPlayer_1.5.1.exe" export_path="../../MusicPlayer_1.5.1.exe"
patches=PackedStringArray() patches=PackedStringArray()
encryption_include_filters="" encryption_include_filters=""
@@ -83,7 +83,7 @@ dedicated_server=false
custom_features="" custom_features=""
export_filter="all_resources" export_filter="all_resources"
include_filter="" include_filter=""
exclude_filter="tests/*, addons/gdUnit4/*" exclude_filter="tests/*, addons/gdUnit4/*, linux/*"
export_path="./MusicPlayer_0.9_Beta.dmg" export_path="./MusicPlayer_0.9_Beta.dmg"
patches=PackedStringArray() patches=PackedStringArray()
encryption_include_filters="" encryption_include_filters=""
@@ -343,7 +343,7 @@ dedicated_server=false
custom_features="" custom_features=""
export_filter="all_resources" export_filter="all_resources"
include_filter="" include_filter=""
exclude_filter="tests/*, addons/gdUnit4/*" exclude_filter="tests/*, addons/gdUnit4/*, linux/*"
export_path="web/index.html" export_path="web/index.html"
patches=PackedStringArray() patches=PackedStringArray()
encryption_include_filters="" encryption_include_filters=""
@@ -390,7 +390,7 @@ dedicated_server=false
custom_features="" custom_features=""
export_filter="all_resources" export_filter="all_resources"
include_filter="" include_filter=""
exclude_filter="tests/*, addons/gdUnit4/*" exclude_filter="tests/*, addons/gdUnit4/*, linux/*"
export_path="./MusicPlayer_1.5.1.x86_64" export_path="./MusicPlayer_1.5.1.x86_64"
patches=PackedStringArray() patches=PackedStringArray()
encryption_include_filters="" encryption_include_filters=""
+116 -1
View File
@@ -1 +1,116 @@
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><g transform="translate(32 32)"><path d="m-16-32c-8.86 0-16 7.13-16 15.99v95.98c0 8.86 7.13 15.99 16 15.99h96c8.86 0 16-7.13 16-15.99v-95.98c0-8.85-7.14-15.99-16-15.99z" fill="#363d52"/><path d="m-16-32c-8.86 0-16 7.13-16 15.99v95.98c0 8.86 7.13 15.99 16 15.99h96c8.86 0 16-7.13 16-15.99v-95.98c0-8.85-7.14-15.99-16-15.99zm0 4h96c6.64 0 12 5.35 12 11.99v95.98c0 6.64-5.35 11.99-12 11.99h-96c-6.64 0-12-5.35-12-11.99v-95.98c0-6.64 5.36-11.99 12-11.99z" fill-opacity=".4"/></g><g stroke-width="9.92746" transform="matrix(.10073078 0 0 .10073078 12.425923 2.256365)"><path d="m0 0s-.325 1.994-.515 1.976l-36.182-3.491c-2.879-.278-5.115-2.574-5.317-5.459l-.994-14.247-27.992-1.997-1.904 12.912c-.424 2.872-2.932 5.037-5.835 5.037h-38.188c-2.902 0-5.41-2.165-5.834-5.037l-1.905-12.912-27.992 1.997-.994 14.247c-.202 2.886-2.438 5.182-5.317 5.46l-36.2 3.49c-.187.018-.324-1.978-.511-1.978l-.049-7.83 30.658-4.944 1.004-14.374c.203-2.91 2.551-5.263 5.463-5.472l38.551-2.75c.146-.01.29-.016.434-.016 2.897 0 5.401 2.166 5.825 5.038l1.959 13.286h28.005l1.959-13.286c.423-2.871 2.93-5.037 5.831-5.037.142 0 .284.005.423.015l38.556 2.75c2.911.209 5.26 2.562 5.463 5.472l1.003 14.374 30.645 4.966z" fill="#fff" transform="matrix(4.162611 0 0 -4.162611 919.24059 771.67186)"/><path d="m0 0v-47.514-6.035-5.492c.108-.001.216-.005.323-.015l36.196-3.49c1.896-.183 3.382-1.709 3.514-3.609l1.116-15.978 31.574-2.253 2.175 14.747c.282 1.912 1.922 3.329 3.856 3.329h38.188c1.933 0 3.573-1.417 3.855-3.329l2.175-14.747 31.575 2.253 1.115 15.978c.133 1.9 1.618 3.425 3.514 3.609l36.182 3.49c.107.01.214.014.322.015v4.711l.015.005v54.325c5.09692 6.4164715 9.92323 13.494208 13.621 19.449-5.651 9.62-12.575 18.217-19.976 26.182-6.864-3.455-13.531-7.369-19.828-11.534-3.151 3.132-6.7 5.694-10.186 8.372-3.425 2.751-7.285 4.768-10.946 7.118 1.09 8.117 1.629 16.108 1.846 24.448-9.446 4.754-19.519 7.906-29.708 10.17-4.068-6.837-7.788-14.241-11.028-21.479-3.842.642-7.702.88-11.567.926v.006c-.027 0-.052-.006-.075-.006-.024 0-.049.006-.073.006v-.006c-3.872-.046-7.729-.284-11.572-.926-3.238 7.238-6.956 14.642-11.03 21.479-10.184-2.264-20.258-5.416-29.703-10.17.216-8.34.755-16.331 1.848-24.448-3.668-2.35-7.523-4.367-10.949-7.118-3.481-2.678-7.036-5.24-10.188-8.372-6.297 4.165-12.962 8.079-19.828 11.534-7.401-7.965-14.321-16.562-19.974-26.182 4.4426579-6.973692 9.2079702-13.9828876 13.621-19.449z" fill="#478cbf" transform="matrix(4.162611 0 0 -4.162611 104.69892 525.90697)"/><path d="m0 0-1.121-16.063c-.135-1.936-1.675-3.477-3.611-3.616l-38.555-2.751c-.094-.007-.188-.01-.281-.01-1.916 0-3.569 1.406-3.852 3.33l-2.211 14.994h-31.459l-2.211-14.994c-.297-2.018-2.101-3.469-4.133-3.32l-38.555 2.751c-1.936.139-3.476 1.68-3.611 3.616l-1.121 16.063-32.547 3.138c.015-3.498.06-7.33.06-8.093 0-34.374 43.605-50.896 97.781-51.086h.066.067c54.176.19 97.766 16.712 97.766 51.086 0 .777.047 4.593.063 8.093z" fill="#478cbf" transform="matrix(4.162611 0 0 -4.162611 784.07144 817.24284)"/><path d="m0 0c0-12.052-9.765-21.815-21.813-21.815-12.042 0-21.81 9.763-21.81 21.815 0 12.044 9.768 21.802 21.81 21.802 12.048 0 21.813-9.758 21.813-21.802" fill="#fff" transform="matrix(4.162611 0 0 -4.162611 389.21484 625.67104)"/><path d="m0 0c0-7.994-6.479-14.473-14.479-14.473-7.996 0-14.479 6.479-14.479 14.473s6.483 14.479 14.479 14.479c8 0 14.479-6.485 14.479-14.479" fill="#414042" transform="matrix(4.162611 0 0 -4.162611 367.36686 631.05679)"/><path d="m0 0c-3.878 0-7.021 2.858-7.021 6.381v20.081c0 3.52 3.143 6.381 7.021 6.381s7.028-2.861 7.028-6.381v-20.081c0-3.523-3.15-6.381-7.028-6.381" fill="#fff" transform="matrix(4.162611 0 0 -4.162611 511.99336 724.73954)"/><path d="m0 0c0-12.052 9.765-21.815 21.815-21.815 12.041 0 21.808 9.763 21.808 21.815 0 12.044-9.767 21.802-21.808 21.802-12.05 0-21.815-9.758-21.815-21.802" fill="#fff" transform="matrix(4.162611 0 0 -4.162611 634.78706 625.67104)"/><path d="m0 0c0-7.994 6.477-14.473 14.471-14.473 8.002 0 14.479 6.479 14.479 14.473s-6.477 14.479-14.479 14.479c-7.994 0-14.471-6.485-14.471-14.479" fill="#414042" transform="matrix(4.162611 0 0 -4.162611 656.64056 631.05679)"/></g></svg> <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#6fb1d6"/>
<stop offset="1" stop-color="#2e6a94"/>
</linearGradient>
<radialGradient id="plate" cx="0.42" cy="0.38" r="0.7">
<stop offset="0" stop-color="#ffffff"/>
<stop offset="1" stop-color="#e3ded6"/>
</radialGradient>
<radialGradient id="well" cx="0.45" cy="0.4" r="0.7">
<stop offset="0" stop-color="#fbf9f5"/>
<stop offset="1" stop-color="#e9e3d9"/>
</radialGradient>
<linearGradient id="band" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#4a4a4a"/>
<stop offset="1" stop-color="#1f1f1f"/>
</linearGradient>
<linearGradient id="cup" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#3d3d3d"/>
<stop offset="1" stop-color="#151515"/>
</linearGradient>
<linearGradient id="cookie" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#d99a52"/>
<stop offset="1" stop-color="#b8742f"/>
</linearGradient>
<linearGradient id="falu" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#c63b2f"/>
<stop offset="1" stop-color="#9e2a21"/>
</linearGradient>
</defs>
<!-- Background -->
<rect x="16" y="16" width="480" height="480" rx="108" fill="url(#sky)"/>
<!-- Plate shadow, plate, rim band and well -->
<ellipse cx="262" cy="306" rx="178" ry="174" fill="#1d4a6b" opacity="0.35"/>
<circle cx="256" cy="294" r="172" fill="url(#plate)" stroke="#d6d0c6" stroke-width="3"/>
<circle cx="256" cy="294" r="150" fill="none" stroke="#3b6ea5" stroke-width="7"/>
<circle cx="256" cy="294" r="139" fill="none" stroke="#3b6ea5" stroke-width="2"/>
<circle cx="256" cy="294" r="116" fill="url(#well)" stroke="#ddd6cb" stroke-width="2"/>
<!-- Headphone band arching over the plate -->
<path d="M 92 286 A 164 164 0 0 1 420 286" fill="none" stroke="url(#band)" stroke-width="26" stroke-linecap="round"/>
<path d="M 104 262 A 152 152 0 0 1 408 262" fill="none" stroke="#6a6a6a" stroke-width="4" stroke-linecap="round" opacity="0.6"/>
<!-- Ear cups on the plate's edges -->
<g>
<rect x="58" y="232" width="62" height="118" rx="28" fill="url(#cup)"/>
<rect x="104" y="246" width="28" height="90" rx="14" fill="#2c2c2c" stroke="#ff8c1a" stroke-width="4"/>
<rect x="70" y="252" width="10" height="56" rx="5" fill="#ffffff" opacity="0.15"/>
</g>
<g>
<rect x="392" y="232" width="62" height="118" rx="28" fill="url(#cup)"/>
<rect x="380" y="246" width="28" height="90" rx="14" fill="#2c2c2c" stroke="#ff8c1a" stroke-width="4"/>
<rect x="404" y="252" width="10" height="56" rx="5" fill="#ffffff" opacity="0.15"/>
</g>
<!-- Crumbs -->
<g fill="#c98a45">
<circle cx="178" cy="370" r="4"/>
<circle cx="190" cy="381" r="2.5"/>
<circle cx="336" cy="378" r="3.5"/>
<circle cx="326" cy="214" r="2.5"/>
<circle cx="184" cy="226" r="3"/>
</g>
<!-- House-shaped cookie: thick baked edge, then falu red icing -->
<g transform="translate(256 288) scale(1.12) translate(-256 -288)">
<!-- shadow -->
<path d="M 256 192 L 344 270 L 334 270 L 334 368 L 178 368 L 178 270 L 168 270 Z"
fill="#7a4a1e" opacity="0.28" stroke="#7a4a1e" stroke-width="16" stroke-linejoin="round"
transform="translate(5 7)"/>
<!-- chimney, baked -->
<rect x="290" y="206" width="22" height="40" rx="6" fill="#c98a45" stroke="#c98a45" stroke-width="10" stroke-linejoin="round"/>
<!-- cookie body with a thick, rounded baked edge -->
<path d="M 256 192 L 344 270 L 334 270 L 334 368 L 178 368 L 178 270 L 168 270 Z"
fill="url(#cookie)" stroke="#c98a45" stroke-width="16" stroke-linejoin="round"/>
<path d="M 256 192 L 344 270 L 334 270 L 334 368 L 178 368 L 178 270 L 168 270 Z"
fill="none" stroke="#a8682a" stroke-width="2" stroke-linejoin="round" transform="translate(0 0)" opacity="0.6"/>
<rect x="295" y="214" width="12" height="26" rx="2" fill="#3a3a3a"/>
<!-- roof icing -->
<path d="M 256 210 L 324 270 L 188 270 Z" fill="#3e3e46"/>
<!-- walls icing, falu red -->
<rect x="190" y="272" width="132" height="86" rx="4" fill="url(#falu)"/>
<!-- white roof edge piping -->
<path d="M 182 274 L 256 208 L 330 274" fill="none" stroke="#fdfbf7" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
<!-- white corner boards -->
<rect x="190" y="272" width="8" height="86" fill="#fdfbf7"/>
<rect x="314" y="272" width="8" height="86" fill="#fdfbf7"/>
<!-- windows with white frames and crosses -->
<rect x="210" y="288" width="30" height="30" rx="2" fill="#2f5d86" stroke="#fdfbf7" stroke-width="5"/>
<path d="M 225 288 V 318 M 210 303 H 240" stroke="#fdfbf7" stroke-width="3"/>
<rect x="272" y="288" width="30" height="30" rx="2" fill="#2f5d86" stroke="#fdfbf7" stroke-width="5"/>
<path d="M 287 288 V 318 M 272 303 H 302" stroke="#fdfbf7" stroke-width="3"/>
<!-- door with white frame -->
<rect x="242" y="320" width="28" height="38" rx="2" fill="#7e1f18" stroke="#fdfbf7" stroke-width="4"/>
<circle cx="264" cy="340" r="2.5" fill="#f2c14e"/>
<!-- gable window in the roof -->
<circle cx="256" cy="246" r="8" fill="#f2c14e" stroke="#fdfbf7" stroke-width="3"/>
<!-- dotted icing along the cookie's bottom edge -->
<g fill="#fdfbf7">
<circle cx="196" cy="368" r="3"/>
<circle cx="214" cy="368" r="3"/>
<circle cx="232" cy="368" r="3"/>
<circle cx="250" cy="368" r="3"/>
<circle cx="268" cy="368" r="3"/>
<circle cx="286" cy="368" r="3"/>
<circle cx="304" cy="368" r="3"/>
<circle cx="322" cy="368" r="3"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

+6
View File
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.cte
compress/mode=0 compress/mode=0
compress/high_quality=false compress/high_quality=false
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1 compress/hdr_compression=1
compress/normal_map=0 compress/normal_map=0
compress/channel_pack=0 compress/channel_pack=0
@@ -25,6 +27,10 @@ mipmaps/generate=false
mipmaps/limit=-1 mipmaps/limit=-1
roughness/mode=0 roughness/mode=0
roughness/src_normal="" roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true process/fix_alpha_border=true
process/premult_alpha=false process/premult_alpha=false
process/normal_map_invert_y=false process/normal_map_invert_y=false
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Installs Music Player Randomizer for the current user, with an app menu entry and icon.
# Run it from the unpacked release folder: ./install.sh
set -euo pipefail
cd "$(dirname "$0")"
bin_dir="${XDG_BIN_HOME:-$HOME/.local/bin}"
data_dir="${XDG_DATA_HOME:-$HOME/.local/share}"
binary="$bin_dir/music-player-randomizer"
icon_dir="$data_dir/icons/hicolor/256x256/apps"
mkdir -p "$bin_dir" "$icon_dir" "$data_dir/applications"
install -m755 MusicPlayer.x86_64 "$binary"
install -m644 music-player-randomizer.png "$icon_dir/music-player-randomizer.png"
# The menu entry needs the full path, since ~/.local/bin is not always on PATH
sed "s|^Exec=.*|Exec=$binary|" music-player-randomizer.desktop \
> "$data_dir/applications/music-player-randomizer.desktop"
# Refresh menu and icon caches where the tools exist; most desktops also notice on their own
command -v update-desktop-database > /dev/null \
&& update-desktop-database "$data_dir/applications" || true
command -v gtk-update-icon-cache > /dev/null \
&& gtk-update-icon-cache -q "$data_dir/icons/hicolor" || true
echo "Installed. Start it from your app menu, or run: $binary"
echo "To uninstall, delete:"
echo " $binary"
echo " $data_dir/applications/music-player-randomizer.desktop"
echo " $icon_dir/music-player-randomizer.png"
+8
View File
@@ -0,0 +1,8 @@
[Desktop Entry]
Type=Application
Name=Music Player Randomizer
Comment=Guess the video game soundtrack with your friends
Exec=music-player-randomizer
Icon=music-player-randomizer
Terminal=false
Categories=Game;Audio;
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Packages an exported Linux build with its icon, app menu entry and install script.
#
# Usage: linux/package.sh <exported binary> <version> <output dir>
# Creates <output dir>/MusicPlayer_<version>_linux.tar.gz. Godot is taken from $GODOT,
# or `godot` on PATH, to render the icon.
set -euo pipefail
if [[ $# -ne 3 ]]; then
echo "Usage: $0 <exported binary> <version> <output dir>" >&2
exit 1
fi
binary="$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
version="$2"
out_dir="$(mkdir -p "$3" && cd "$3" && pwd)"
linux_dir="$(cd "$(dirname "$0")" && pwd)"
project_dir="$(dirname "$linux_dir")"
godot_bin="${GODOT:-godot}"
staging="$(mktemp -d)"
trap 'rm -rf "$staging"' EXIT
package_dir="$staging/MusicPlayer"
mkdir -p "$package_dir"
install -m755 "$binary" "$package_dir/MusicPlayer.x86_64"
install -m755 "$linux_dir/install.sh" "$package_dir/install.sh"
install -m644 "$linux_dir/music-player-randomizer.desktop" "$package_dir/"
# Run outside the project so Godot only renders the icon
(cd "$staging" && "$godot_bin" --headless -s "$linux_dir/render_icon.gd" -- \
"$project_dir/icon.svg" "$package_dir/music-player-randomizer.png" 256)
archive="$out_dir/MusicPlayer_${version}_linux.tar.gz"
tar -czf "$archive" -C "$staging" MusicPlayer
echo "Created $archive"
+24
View File
@@ -0,0 +1,24 @@
extends SceneTree
## Renders an SVG to a square PNG, used to package the icon for Linux.
##
## Usage: godot --headless -s render_icon.gd -- <input.svg> <output.png> <size>
func _initialize() -> void:
var args: PackedStringArray = OS.get_cmdline_user_args()
if args.size() != 3:
printerr("Usage: godot --headless -s render_icon.gd -- <input.svg> <output.png> <size>")
quit(1)
return
var svg: String = FileAccess.get_file_as_string(args[0])
var source: Image = Image.new()
# Load at 1x to read the SVG's own size, then scale so the result is <size> pixels
var error: int = source.load_svg_from_string(svg)
if error == OK:
var image: Image = Image.new()
error = image.load_svg_from_string(svg, float(args[2]) / source.get_width())
if error == OK:
error = image.save_png(args[1])
if error != OK:
printerr("Could not render %s to %s (error %d)" % [args[0], args[1], error])
quit(error)
+1
View File
@@ -0,0 +1 @@
uid://b51oybupn7ns7