From e2c69fe93ed6512c442f29b65ad0badd6bc6e35d Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Sun, 12 Oct 2025 02:08:51 -0400 Subject: [PATCH 01/11] Initial app image test --- .github/workflows/dotnet.yml | 131 ++++++++++-- .gitignore | 2 +- setup/AppImage.pupnet.conf | 200 ++++++++++++++++++ setup/AppImageBundleFiles.sh | 18 ++ setup/Icons/icon.128.png | Bin 0 -> 974 bytes setup/Icons/icon.16.png | Bin 0 -> 350 bytes setup/Icons/icon.24.png | Bin 0 -> 606 bytes setup/Icons/icon.256.png | Bin 0 -> 1416 bytes setup/Icons/icon.32.png | Bin 0 -> 395 bytes setup/Icons/icon.48.png | Bin 0 -> 648 bytes setup/Icons/icon.512.png | Bin 0 -> 11638 bytes setup/Icons/icon.64.png | Bin 0 -> 734 bytes setup/Icons/icon.svg | 83 ++++++++ setup/LinuxBuildZipper.ps1 | 60 ------ setup/Smz3CasRandomizer.desktop | 15 ++ setup/app.metainfo.xml | 42 ++++ .../OptionsWindowRandomizerOptions.cs | 3 + src/TrackerCouncil.Smz3.UI/App.axaml.cs | 23 ++ src/TrackerCouncil.Smz3.UI/Program.cs | 28 ++- .../Services/MainWindowService.cs | 4 +- .../MultiplayerConnectWindowService.cs | 15 +- .../Services/OptionsWindowService.cs | 6 + .../TrackerCouncil.Smz3.UI.csproj | 86 ++++---- .../ViewModels/SetupWindowViewModel.cs | 2 + .../Views/AboutWindow.axaml.cs | 9 +- .../Views/SetupWindow.axaml | 6 + .../Views/SetupWindow.axaml.cs | 7 + 27 files changed, 596 insertions(+), 144 deletions(-) create mode 100644 setup/AppImage.pupnet.conf create mode 100755 setup/AppImageBundleFiles.sh create mode 100644 setup/Icons/icon.128.png create mode 100644 setup/Icons/icon.16.png create mode 100644 setup/Icons/icon.24.png create mode 100644 setup/Icons/icon.256.png create mode 100644 setup/Icons/icon.32.png create mode 100644 setup/Icons/icon.48.png create mode 100644 setup/Icons/icon.512.png create mode 100644 setup/Icons/icon.64.png create mode 100644 setup/Icons/icon.svg delete mode 100644 setup/LinuxBuildZipper.ps1 create mode 100644 setup/Smz3CasRandomizer.desktop create mode 100644 setup/app.metainfo.xml diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 7c0135a2d..8e84d9011 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -2,13 +2,16 @@ name: .NET Build + Test + Publish on: push: - branches: [main] + branches: [main, linux-app-image] pull_request: branches: [main] jobs: - build: - runs-on: windows-latest + build-windows: + runs-on: windows-2022 + + outputs: + version-number: ${{ steps.version.outputs.number}} steps: - uses: actions/checkout@v4 @@ -66,9 +69,6 @@ jobs: $version = $version -replace "\+.*", "" Write-Output "number=$version" >> $env:GITHUB_OUTPUT shell: pwsh - - name: Publish Linux 64bit - if: ${{ github.event_name != 'pull_request' }} - run: dotnet publish --os linux --arch x64 -c Release --self-contained false src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj - name: Publish Multiplayer Server if: ${{ github.event_name != 'pull_request' }} run: dotnet publish -c Release --self-contained false src//TrackerCouncil.Smz3.Multiplayer.Server//TrackerCouncil.Smz3.Multiplayer.Server.csproj @@ -76,11 +76,6 @@ jobs: if: ${{ github.event_name != 'pull_request' }} run: '"%programfiles(x86)%/Inno Setup 6/iscc.exe" "setup/randomizer.app.iss"' shell: cmd - - name: Building the Linux 64bit package - if: ${{ github.event_name != 'pull_request' }} - working-directory: setup - run: "./LinuxBuildZipper.ps1" - shell: pwsh - name: Building the Multiplayer Server package if: ${{ github.event_name != 'pull_request' }} working-directory: setup @@ -91,7 +86,79 @@ jobs: if: ${{ github.event_name != 'pull_request' }} with: path: "setup/Output/*" - name: SMZ3CasRandomizer_${{ steps.version.outputs.number }} + name: SMZ3CasRandomizerWindows + + build-linux: + runs-on: ubuntu-22.04 + + needs: [build-windows] + + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + - name: Download config repo + uses: actions/checkout@v4 + with: + repository: TheTrackerCouncil/SMZ3CasConfigs + path: configs + ref: main + - name: Download sprite repo + uses: actions/checkout@v4 + with: + repository: TheTrackerCouncil/SMZ3CasSprites + path: sprites + ref: main + - name: Download tracker sprite repo + uses: actions/checkout@v4 + with: + repository: TheTrackerCouncil/TrackerSprites + path: trackersprites + ref: main + - name: Download git trees + if: ${{ github.event_name != 'pull_request' }} + shell: pwsh + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + $headers = @{ + Authorization="Bearer $Env:GH_TOKEN" + } + Invoke-RestMethod -Uri https://api.github.com/repos/TheTrackerCouncil/SMZ3CasSprites/git/trees/main?recursive=1 -OutFile sprites/Sprites/sprites.json -Headers $headers + Invoke-RestMethod -Uri https://api.github.com/repos/TheTrackerCouncil/TrackerSprites/git/trees/main?recursive=1 -OutFile trackersprites/tracker-sprites.json -Headers $headers + Remove-Item -LiteralPath "trackersprites/.git" -Force -Recurse + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Update VersionOverride in source file + run: | + pwd + VERSION="${{ needs.build-windows.outputs.version-number }}" + BASE_VERSION="${VERSION%%-*}" + FILE="src/TrackerCouncil.Smz3.UI/App.axaml.cs" + sed -i -E "s|^[[:space:]]*private static readonly string\?[[:space:]]+s_versionOverride[[:space:]]*=[[:space:]]*null;|private static readonly string? s_versionOverride = \"${VERSION}\";|" "$FILE" + sed -i "s/^AppVersionRelease *= *.*/AppVersionRelease = ${BASE_VERSION}/" setup/AppImage.pupnet.conf + echo "Updated VersionOverride to: ${VERSION}" + - name: Install PupNet + run: dotnet tool install -g KuiperZone.PupNet + - name: Download AppImageTool + run: | + wget -P "$HOME/.local/bin" "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" + chmod +x "$HOME/.local/bin/appimagetool-x86_64.AppImage" + appimagetool-x86_64.AppImage --version + - name: Run PupNet + run: | + chmod +x setup/AppImageBundleFiles.sh + pupnet setup/AppImage.pupnet.conf --kind appimage -y + - name: Upload artifact + uses: actions/upload-artifact@v4 + if: ${{ github.event_name != 'pull_request' }} + with: + path: "setup/Output/SMZ3CasRandomizer.x86_64*" + name: SMZ3CasRandomizerLinux + build-mac: runs-on: macos-latest if: ${{ github.event_name != 'pull_request' }} @@ -154,4 +221,42 @@ jobs: uses: actions/upload-artifact@v4 with: path: "setup/output/*" - name: SMZ3CasRandomizerMacOS_${{ steps.version.outputs.number }} + name: SMZ3CasRandomizerMacOS + + package: + runs-on: ubuntu-22.04 + + needs: [build-windows, build-linux, build-mac] + + permissions: + contents: write + + steps: + - uses: actions/download-artifact@v5 + with: + name: SMZ3CasRandomizerWindows + path: out + - uses: actions/download-artifact@v5 + with: + name: SMZ3CasRandomizerLinux + path: out + - uses: actions/download-artifact@v5 + with: + name: SMZ3CasRandomizerMacOS + path: out + - name: Extract some files + run: | + ls -alR + - name: Upload artifact + uses: actions/upload-artifact@v4 + if: ${{ github.event_name != 'pull_request' }} + with: + path: "out/*" + name: SMZ3CasRandomizer_${{ needs.build-windows.outputs.version-number }} + - name: Delete old artifacts + uses: geekyeggo/delete-artifact@v5 + with: + name: | + SMZ3CasRandomizerWindows + SMZ3CasRandomizerLinux + SMZ3CasRandomizerMacOS diff --git a/.gitignore b/.gitignore index 5ce039cab..a7b21a131 100644 --- a/.gitignore +++ b/.gitignore @@ -345,7 +345,7 @@ healthchecksdb /Randomizer.CLI/Properties/launchSettings.json /WebRandomizer/randomizer.db /asar/ -setup/Output/ +[Ss]etup/[Oo]utput/ patch-config* **/.DS_Store **/*.db diff --git a/setup/AppImage.pupnet.conf b/setup/AppImage.pupnet.conf new file mode 100644 index 000000000..18f3662c0 --- /dev/null +++ b/setup/AppImage.pupnet.conf @@ -0,0 +1,200 @@ +################################################################################ +# PUPNET DEPLOY: 1.9.0 +################################################################################ + +######################################## +# APP PREAMBLE +######################################## + +# Mandatory application base name. This MUST BE the base name of the main executable file. It should NOT +# include any directory part or extension, i.e. do not append '.exe' or '.dll'. It should not contain +# spaces or invalid filename characters. +AppBaseName = SMZ3CasRandomizer + +# Mandatory application friendly name. +AppFriendlyName = "SMZ3 Cas' Randomizer" + +# Mandatory application ID in reverse DNS form. The value should stay constant for lifetime of the software. +AppId = org.trackercouncil.smz3 + +# Mandatory application version and package release of form: 'VERSION[RELEASE]'. Use optional square +# brackets to denote package release, i.e. '1.2.3[1]'. Release refers to a change to the deployment +# package, rather the application. If release part is absent (i.e. '1.2.3'), the release value defaults +# to '1'. Note that the version-release value given here may be overridden from the command line. +AppVersionRelease = 1.0.0 + +# Mandatory single line application summary text in default (English) language. +AppShortSummary = "Casual standalone version of the SMZ3 randomizer" + +# Multi-line (surround with triple """ quotes) application description which provides longer explanation +# than AppShortSummary in default language. Optional but it is recommended to specify this. Text +# separated by an empty line will be treated as separate paragraphs. Avoid complex formatting, and do not +# use HTML or markdown, other than list items beginning with "* ", "+ " or "- ". This content is +# used by package builders where supported, including RPM and DEB, and is used to populate the +# ${APPSTREAM_DESCRIPTION_XML} element used within AppStream metadata. +AppDescription = """ + UI application for selecting, randomizing, and shuffling MSUs for various rom hacks and randomizers. +""" + +# Mandatory application license ID. This should be one of the recognized SPDX license +# identifiers, such as: 'MIT', 'GPL-3.0-or-later' or 'Apache-2.0'. For a proprietary or +# custom license, use 'LicenseRef-Proprietary' or 'LicenseRef-LICENSE'. +AppLicenseId = MIT + +# Optional path to application copyright/license text file. If provided, it will be packaged with the +# application and used with package builders where supported. +AppLicenseFile = ../LICENSE + +# Optional path to application changelog file. IMPORTANT. If given, this file should contain version +# information in a predefined format. Namely, it should contain one or more version headings of form: +# '+ VERSION;DATE', under which are to be listed change items of form: '- Change description'. Formatted +# information will be parsed and used to expand the ${APPSTREAM_CHANGELOG_XML} macro used +# for AppStream metadata (superfluous text is ignored, so the file may also contain README information). +# The given file will also be packaged with the application verbatim. See: https://github.com/kuiperzone/PupNet-Deploy. +AppChangeFile = + +######################################## +# PUBLISHER +######################################## + +# Mandatory publisher, group or creator name. +PublisherName = The Tracker Council + +# Publisher ID in reverse DNS form. Invariably, this would be the same as AppId, excluding the app leaf +# name. The value populates the ${PUBLISHER_ID} macro used AppStream metainfo. If omitted, defaults to +# the leading parts of AppId. It is highly recommended to specify the value explicitly. +PublisherId = org.trackercouncil + +# Optional copyright statement. +PublisherCopyright = + +# Optional publisher or application web-link name. Note that Windows Setup packages +# require both PublisherLinkName and PublisherLinkUrl in order to include the link as +# an item in program menu entries. Do not modify name, as may leave old entries in updated installations. +PublisherLinkName = Home Page + +# Publisher or application web-link URL. Although optional, it should be considered mandatory if using +# MetaFile +PublisherLinkUrl = https://github.com/TheTrackerCouncil + +# Publisher or maintainer email contact. Although optional, some package builders (i.e. DEB) require it +# and may warn or fail unless provided. +PublisherEmail = + +######################################## +# DESKTOP INTEGRATION +######################################## + +# Boolean (true or false) which indicates whether the application is hidden on the desktop. It is used to +# populate the 'NoDisplay' field of the .desktop file. The default is false. Setting to true will also +# cause the main application start menu entry to be omitted for Windows Setup. +DesktopNoDisplay = false + +# Boolean (true or false) which indicates whether the application runs in the terminal, rather than +# providing a GUI. It is used to populate the 'Terminal' field of the .desktop file. +DesktopTerminal = false + +# Optional path to a Linux desktop file. If empty (default), one will be generated automatically from +# the information in this file. Supplying a custom file, however, allows for mime-types and +# internationalisation. If supplied, the file MUST contain the line: 'Exec=${INSTALL_EXEC}' +# in order to use the correct install location. Other macros may be used to help automate the content. +# Note. PupNet Deploy can generate you a desktop file. Use --help and 'pupnet --help macro' for reference. +# See: https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html +DesktopFile = Smz3CasRandomizer.desktop + +# Optional command name to start the application from the terminal. If, for example, AppBaseName is +# 'Zone.Kuiper.HelloWorld', the value here may be set to a simpler and/or lower-case variant such as +# 'helloworld'. It must not contain spaces or invalid filename characters. Do not add any extension such +# as '.exe'. If empty, the application will not be in the path and cannot be started from the command line. +# For Windows Setup packages, see also SetupCommandPrompt. StartCommand is not +# supported for all packages kinds (i.e. Flatpak). Default is empty (none). +StartCommand = SMZ3CasRandomizer + +# Optional category for the application. The value should be one of the recognized Freedesktop top-level +# categories, such as: Audio, Development, Game, Office, Utility etc. Only a single value should be +# provided here which will be used, where supported, to populate metadata. The default is empty. +# See: https://specifications.freedesktop.org/menu-spec/latest/apa.html +PrimeCategory = Game + +# Path to AppStream metadata file. It is optional, but recommended as it is used by software centers. +# Note. The contents of the files may use macro variables. Use 'pupnet --help macro' for reference. +# See: https://docs.appimage.org/packaging-guide/optional/appstream.html +MetaFile = app.metainfo.xml + +# Optional icon file paths. The value may include multiple filenames separated with semicolon or given +# in multi-line form. Valid types are SVG, PNG and ICO (ICO ignored on Linux). Note that the inclusion +# of a scalable SVG is preferable on Linux, whereas PNGs must be one of the standard sizes and MUST +# include the size in the filename in the form: name.32x32.png' or 'name.32.png'. +IconFiles = """ + Icons/icon.512.png + Icons/icon.256.png + Icons/icon.128.png + Icons/icon.64.png + Icons/icon.48.png + Icons/icon.32.png + Icons/icon.24.png + Icons/icon.16.png + Icons/icon.svg +""" + +######################################## +# DOTNET PUBLISH +######################################## + +# Optional path relative to this file in which to find the dotnet project (.csproj) file, or the +# directory containing it. If empty (default), a single project file is expected under the same +# directory as this file. IMPORTANT. If set to 'NONE', dotnet publish is disabled +# (i.e. not called). Instead, only DotnetPostPublish is called. +DotnetProjectPath = ../src/TrackerCouncil.Smz3.UI + +# Optional arguments supplied to 'dotnet publish'. Do NOT include '-r' (runtime), or '-c' (configuration) +# here as they will be added according to command line arguments. Typically you want as a minimum: +# '-p:Version=${APP_VERSION} --self-contained true'. Additional useful arguments include: +# '-p:DebugType=None -p:DebugSymbols=false -p:PublishSingleFile=true -p:PublishReadyToRun=true +# -p:PublishTrimmed=true -p:TrimMode=link'. Note. This value may use macro variables. Use 'pupnet --help macro' +# for reference. See: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish +DotnetPublishArgs = -p:Version=${APP_VERSION} --self-contained true -p:DebugType=None -p:DebugSymbols=false + +DotnetPostPublish = AppImageBundleFiles.sh + +######################################## +# PACKAGE OUTPUT +######################################## + +# Optional package name (excludes version etc.). If empty, defaults to AppBaseName. However, it is +# used not only to specify the base output filename, but to identify the application in DEB and RPM +# packages. You may wish, therefore, to ensure that the value represents a unique name. Naming +# requirements are strict and must contain only alpha-numeric and '-', '+' and '.' characters. +PackageName = SMZ3CasRandomizer + +# Output directory, or subdirectory relative to this file. It will be created if it does not exist and +# will contain the final deploy output files. If empty, it defaults to the location of this file. +OutputDirectory = Output + +######################################## +# APPIMAGE OPTIONS +######################################## + +# Additional arguments for use with appimagetool, i.e. '--no-appstream' to disable pedantic metadata checking. +# Use for signing with '--sign'. Default is empty. +AppImageArgs = + +# Optional path to AppImage fuse runtime(s), but not to be confused with .NET runtime ID. If AppImageRuntimePath is +# left empty (default), appimagetool will download the latest runtime automatically. Specifying a path here avoids +# the need for an internet connection during build, and fixes the runtime version. If AppImageRuntimePath +# points to a file, the value is supplied directly to appimagetool using '--runtime-file'. If it points to a +# directory, the directory should contain expected runtimes, which will be selected for the target architecture, +# namely one of: runtime-aarch64, runtime-armhf, runtime-i686 or runtime-x86_64. Runtimes can be downloaded: +# https://github.com/AppImage/type2-runtime/releases +AppImageRuntimePath = + +# Boolean (true or false) which sets whether to include the application version in the AppImage filename, +# i.e. 'HelloWorld-1.2.3-x86_64.AppImage'. Default is false. It is ignored if the output filename is +# specified at command line. +AppImageVersionOutput = false + + +FlatpakPlatformRuntime = 1.0.0 +FlatpakPlatformSdk = 1.0.0 +FlatpakPlatformVersion = 1.0.0 +SetupMinWindowsVersion = 1.0.0 \ No newline at end of file diff --git a/setup/AppImageBundleFiles.sh b/setup/AppImageBundleFiles.sh new file mode 100755 index 000000000..3db6346e4 --- /dev/null +++ b/setup/AppImageBundleFiles.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +echo "BUILD_ARCH: ${BUILD_ARCH}" +echo "BUILD_TARGET: ${BUILD_TARGET}" +echo "BUILD_SHARE: ${BUILD_SHARE}" +echo "BUILD_APP_BIN: ${BUILD_APP_BIN}" + +mkdir ${BUILD_APP_BIN}/DefaultData +mv sprites/Sprites ${BUILD_APP_BIN}/DefaultData/Sprites +mv trackersprites ${BUILD_APP_BIN}/DefaultData/TrackerSprites +mv configs/Profiles ${BUILD_APP_BIN}/DefaultData/Configs +mv configs/Schemas ${BUILD_APP_BIN}/DefaultData/Schemas + +UNIQUE_ID=$(uuidgen) +echo "$UNIQUE_ID" >> ${BUILD_APP_BIN}/DefaultData/id.txt + +ls -al ${BUILD_APP_BIN}/DefaultData + diff --git a/setup/Icons/icon.128.png b/setup/Icons/icon.128.png new file mode 100644 index 0000000000000000000000000000000000000000..0e00092c15cc70fb95d30db7300cacab3849cc45 GIT binary patch literal 974 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSjKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCjKus+S48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh z?!xdN1Q+aGJ{c&&S>O>_%)r1c48n{Iv*t(u1=&kHeO=jKa&d`T=`P~@y@P>)S;*7H zF{I+w+q;V8w+%$v63dq>7;GpoxKLoPXkcKl0mL={p$+zm7YsH4`8tn3U+pt7&rP-0 z56M0LwetK`^~i1SX9*h3H@$u*S3m!3ta0K#(+myzNQw~|=Ju!})}frWuFLBwF*5&M?*pE^16k6$>jUSMrtP+(YgvQ{^7y~>BZcDAi= z?|v>`pL?^k`P}Mn2`iXhFgXBqUVh@uHe-^21na)`yL!L3FEVA)Jhr0@rk$gKvnc-Z zNw-e>IrnrWSY5W9PW{s!pDqfr5TqMQl?Gk)NvN1_KZDa}VwBD*pkoynfNnmZu|RE+ z`|h@7f7d0XEQEo!-^j(p+4hZ{vxjC-QM>a5vIPE_w1QdmAi5)^UlV%Z;f(Bi`>rsZ>G*1ufBW&rA1{%- zdtHL{iIDGlGv7T;J92No33MpAlX3e>NZz}>zYUkwLPPG34a+nE*3BUtXY8J7nog~r zUGUr7`MSoEj%&$0vC0deiNWbXyeK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG z?e4OkysZZgMp>+7WOzUQkh1meFS)aX1ZB;# z!Xi3VBDmkjT)TWsM0`{IWG$f+mFKS10R4CDH&4yhE7uQ2R^=8?RS;s+{E=I5=h`Dh i)AtLf*8fkhijtT6@26*Ub*(tiwG5uFelF{r5}E+-n|3(> literal 0 HcmV?d00001 diff --git a/setup/Icons/icon.24.png b/setup/Icons/icon.24.png new file mode 100644 index 0000000000000000000000000000000000000000..7673c8276ae58e994952f679ab7fd9c25e5ff895 GIT binary patch literal 606 zcmV-k0-^nhP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007 zbV*G`2k8n576Ug^sWBk{00E*&L_t(Y$IVo$l7lc5J?$bQBPt^H1CWuGNF)-8tVAMN zhpgBSs69*ez_M)cnx_*0fG@-Vd@R}jmZ&eyoSRz_)Cvnm$wfRWrFElO_ru)!e~9D} z@WmLUlV%R_Sk|jK=;jVzht_k35@@~-5k@J-1FeFtL<$Kw(8kAu_cgw<+=rfE=B z6~4bdZvv_YACo!g^p#Sc{UV1bhIs)nkgt=vFOxIYkJds9A#NtE4@@Wrq5OhmcJuV( sjB!l~wC9DI66nn))t(oc__*VL0o!TVa(gmEP5=M^07*qoM6N<$f&_=`$p8QV literal 0 HcmV?d00001 diff --git a/setup/Icons/icon.256.png b/setup/Icons/icon.256.png new file mode 100644 index 0000000000000000000000000000000000000000..65a7dfee49973f68b867c351b190695a703520e3 GIT binary patch literal 1416 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G!U;i$lZzY=1HA;AcrO0(btiI zVPik{pF~z5Um@8e$d`ekN{xY`p@o6r7f`6-1p`B=0RzLU1O^7H84L{K1#@-<+5jck zlDyqr82*Fcg1yTp14TFsJR*x37`TN&n2}-D90{Nxdx@v7EBi|>E>SBkc4oiJKugbf zx;TbZ+zdwP0efFdXT9r|39lqc?3<9Ai~ z`#ZmU$^Xv7%RIysKte{Zwhoxg!f? zF_eHb9hh(Mo#W9W<$YU^Roj32ePP$b60Z2o@@;A7SBfMbE<-aEYR&$05@}Aofe&6( zcUIwFIR75MvenWbTp_Z2^l;r)8Bkz*VUqrl6#+hs5PJYe*fM$w_3j& zjL4Aycl#ec<=MTu;;J@gH9yL2fBjwjc<)k)k6Td;hsShYnS^Ts~vR zv*3hfw=;gOyJE2P`*}WwKb(kohg<1$bI&nXW98ZM+pB-1KR{GrW4}W`kw@8mL2@mySJ=f#J_xy4CY4!<{`MrHV(vDRdFTM~s`|Pq)o2}&fFV0_iU&k6tK6~Il z)BojRH~tMaGJX%U%VPfooOz*=@NB`^GmMKat~{E!qWHoJPJ3)R9T*zijq1Mv?VEkJ z=WW=}HP2rxoRd30Gfl)wx%ftw_i0y7`I6a|4p^gAfZ@UFXNwQa3_S5Pm!mfV=-U4F z>&xpHvwGg9xs@4z?_W3f)CsJa%b@EfFwmEkXe7UWKY!+tZoeC)_st$=yYXA;E}e4r xo9Pz4;)?9F+}E6krQWZSW%wbCB|FU6G=J;0hxJTfe&#R$fv2mV%Q~loCIIzC&=mjx literal 0 HcmV?d00001 diff --git a/setup/Icons/icon.32.png b/setup/Icons/icon.32.png new file mode 100644 index 0000000000000000000000000000000000000000..43eef33e824914bb07c41cc626864ea793505a6f GIT binary patch literal 395 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%C&rs6b?Si}mUKs7M+SzC z{oH>NSwSk3J%W507^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f-TA0 z-G$*l2rk&Wd@@jkv%n*=n1O*?7=#%aX3dcR3bL1Y`ns~e54*!3}1*UGF9Qt-%ImeD=TcDX?m370sry-uip3Ds^X76fBR1Rc@?M1 d_w*^Htcv?XRnG)x>H)pR;OXk;vd$@?2>=W{jZXjo literal 0 HcmV?d00001 diff --git a/setup/Icons/icon.48.png b/setup/Icons/icon.48.png new file mode 100644 index 0000000000000000000000000000000000000000..d996d473842a941822ac337f46cce3c5cf8d299f GIT binary patch literal 648 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 H^OyeyBbeDI literal 0 HcmV?d00001 diff --git a/setup/Icons/icon.64.png b/setup/Icons/icon.64.png new file mode 100644 index 0000000000000000000000000000000000000000..2ce6b7acea812053e4125e76ef92e0e7e0eec216 GIT binary patch literal 734 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG z?e4ur=`b+dtp&v##iiBl&}J@J=CMW}O*i;^~w=kX+^-YHdOQ;|^Q_0G4g;j;wH zW^I^Ot;K!eW9&P<=`B+~gd{4Q+x738N?6jdDk1LmQ`b-YpX0wOU*2`qT-n)?GdEPQ zIWVo`?GLVBaV^oH+t7*0oBhE}@wfKfPFtVWt-rN#O7s4wQG5oD*LfC9jGw;ro8>&t zUpEq{RYpa8!HW89V-8!yIX zEnxzMEd#^5dn~2T%%?wWWMnw7kmbjq_c&h5H-XW_T9I{)`VdzY=s z-~7_(XkK{0A2#MWI&G_7RlSXSXBV>j#N^GV#h?854zG+m#LUp}{%?a$_e_?y_q>PR z`zK6Nxfqu}tF0%rIyTF0(WB7tXkZ-PvEuqH$0wp}|qA31mzRK^VelO@mYLMSSEB<-1n=cwwYHR0)1A{z;q5IjduH0&lBvU#*?y}vd$@?2>^#&E3E(k literal 0 HcmV?d00001 diff --git a/setup/Icons/icon.svg b/setup/Icons/icon.svg new file mode 100644 index 000000000..33e06e2ed --- /dev/null +++ b/setup/Icons/icon.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/setup/LinuxBuildZipper.ps1 b/setup/LinuxBuildZipper.ps1 deleted file mode 100644 index 72f04eb27..000000000 --- a/setup/LinuxBuildZipper.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -$parentFolder = Split-Path -parent $PSScriptRoot - -# Get publish folder -$folder = "$parentFolder\src\TrackerCouncil.Smz3.UI\bin\Release\net8.0\linux-x64\publish" -$winFolder = "$parentFolder\src\TrackerCouncil.Smz3.UI\bin\Release\net8.0\win-x86\publish" -if (-not (Test-Path $folder)) -{ - $folder = "$parentFolder\src\TrackerCouncil.Smz3.UI\bin\Release\net8.0\publish\linux-x64" - $winFolder = "$parentFolder\src\TrackerCouncil.Smz3.UI\bin\Release\net8.0\publish\win-x86" -} - -# Get version number from TrackerCouncil.Smz3.UI -$version = "0.0.0" -if (Test-Path "$winFolder\TrackerCouncil.Smz3.UI.exe") { - $version = (Get-Item "$winFolder\SMZ3CasRandomizer.exe").VersionInfo.ProductVersion -} -else { - $version = (Get-Item "$folder\SMZ3CasRandomizer.dll").VersionInfo.ProductVersion -} -$version = $version -replace "\+.*", "" - -# Setup default data folder -$dataFolder = "$folder\DefaultData" -New-Item -Path "$dataFolder" -ItemType Directory -Force - -# Copy sprites to be bundled together -if (Test-Path -LiteralPath "$dataFolder\Sprites") { - Remove-Item -LiteralPath "$dataFolder\Sprites" -Recurse -} -Copy-Item "$parentFolder\sprites\Sprites\" -Destination "$dataFolder\Sprites" -Recurse - -# Copy tracker sprites to be bundled together -if (Test-Path -LiteralPath "$dataFolder\TrackerSprites") { - Remove-Item -LiteralPath "$dataFolder\TrackerSprites" -Recurse -} -Copy-Item "$parentFolder\trackersprites\" -Destination "$dataFolder\TrackerSprites" -Recurse - -# Copy configs to be bundled together -if (Test-Path -LiteralPath "$dataFolder\Configs") { - Remove-Item -LiteralPath "$dataFolder\Configs" -Recurse -} -Copy-Item "$parentFolder\configs\Profiles\" -Destination "$dataFolder\Configs" -Recurse - -# Copy schemas to be bundled together -if (Test-Path -LiteralPath "$dataFolder\Schemas") { - Remove-Item -LiteralPath "$dataFolder\Schemas" -Recurse -} -Copy-Item "$parentFolder\configs\Schemas\" -Destination "$dataFolder\Schemas" -Recurse - -# Create package -$fullVersion = "SMZ3CasRandomizerLinux_$version" -$outputFile = "$PSScriptRoot\Output\$fullVersion.tar.gz" -if (Test-Path $outputFile) { - Remove-Item $outputFile -Force -} -if (-not (Test-Path $outputFile)) { - Set-Location $folder - tar -cvzf $outputFile * -} -Set-Location $PSScriptRoot \ No newline at end of file diff --git a/setup/Smz3CasRandomizer.desktop b/setup/Smz3CasRandomizer.desktop new file mode 100644 index 000000000..e6c19fd40 --- /dev/null +++ b/setup/Smz3CasRandomizer.desktop @@ -0,0 +1,15 @@ +[Desktop Entry] +Type=Application +Name=${APP_FRIENDLY_NAME} +Icon=${APP_ID} +StartupWMClass=${APP_BASE_NAME} +Comment=${APP_SHORT_SUMMARY} +Exec=${INSTALL_EXEC} +NoDisplay=${DESKTOP_NODISPLAY} +Terminal=${DESKTOP_TERMINAL} +Categories=${PRIME_CATEGORY} +X-AppImage-Name=${APP_ID}; +X-AppImage-Version=${APP_VERSION}; +X-AppImage-Arch=${PACKAGE_ARCH}; +MimeType= +Keywords= \ No newline at end of file diff --git a/setup/app.metainfo.xml b/setup/app.metainfo.xml new file mode 100644 index 000000000..700c619ec --- /dev/null +++ b/setup/app.metainfo.xml @@ -0,0 +1,42 @@ + + + MIT + + ${APP_ID} + ${APP_FRIENDLY_NAME} +

${APP_SHORT_SUMMARY} + ${PUBLISHER_LINK_URL} + ${APP_LICENSE_ID} + + + + ${PUBLISHER_NAME} + + + ${APP_ID}.desktop + + + ${APPSTREAM_DESCRIPTION_XML} + + + + + ${PRIME_CATEGORY} + + + + development + programming + + + + + ${APPSTREAM_CHANGELOG_XML} + + + \ No newline at end of file diff --git a/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowRandomizerOptions.cs b/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowRandomizerOptions.cs index 16ca2e4c2..557e6d955 100644 --- a/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowRandomizerOptions.cs +++ b/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowRandomizerOptions.cs @@ -94,6 +94,9 @@ public bool DisplayMsuWarning [DynamicFormFieldButton(buttonText: "Update Sprites", groupName: "Bottom right", alignment: DynamicFormAlignment.Right)] public event EventHandler? UpdateSpritesButtonPressed; + + [DynamicFormFieldButton(buttonText: "Create Desktop File", groupName: "Bottom right", alignment: DynamicFormAlignment.Right, platforms: DynamicFormPlatform.Linux)] + public event EventHandler? CreateDesktopFileButtonPressed; #pragma warning restore CS0067 // Event is never used public event PropertyChangedEventHandler? PropertyChanged; diff --git a/src/TrackerCouncil.Smz3.UI/App.axaml.cs b/src/TrackerCouncil.Smz3.UI/App.axaml.cs index 1266eaa49..400c20f22 100644 --- a/src/TrackerCouncil.Smz3.UI/App.axaml.cs +++ b/src/TrackerCouncil.Smz3.UI/App.axaml.cs @@ -1,11 +1,16 @@ using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.Versioning; using System.Threading.Tasks; +using AppImageDesktopFileCreator; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using AvaloniaControls.Controls; using Microsoft.Extensions.DependencyInjection; using SharpHook; +using TrackerCouncil.Smz3.Shared; using TrackerCouncil.Smz3.UI.Views; namespace TrackerCouncil.Smz3.UI; @@ -14,6 +19,16 @@ public partial class App : Application { private IGlobalHook? _hook; private Task? _hookRunner; + private static readonly string? s_versionOverride = null; + + public static string Version + { + get + { + var version = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly()!.Location); + return s_versionOverride ?? (version.ProductVersion ?? "").Split("+")[0]; + } + } public override void Initialize() { @@ -52,4 +67,12 @@ private void NativeMenuItem_OnClick(object? sender, EventArgs e) { Program.MainHost?.Services.GetService()?.Show(); } + + [SupportedOSPlatform("linux")] + internal static CreateDesktopFileResponse BuildLinuxDesktopFile() + { + return new DesktopFileBuilder("org.trackercouncil.smz3", "SMZ3 Cas' Randomizer") + .AddUninstallAction(Directories.AppDataFolder) + .Build(); + } } diff --git a/src/TrackerCouncil.Smz3.UI/Program.cs b/src/TrackerCouncil.Smz3.UI/Program.cs index a8a35a98d..984c2c0c1 100644 --- a/src/TrackerCouncil.Smz3.UI/Program.cs +++ b/src/TrackerCouncil.Smz3.UI/Program.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; +// ReSharper disable once RedundantUsingDirective using System.Linq; using System.Reflection; using System.Threading; @@ -17,7 +17,6 @@ using MSURandomizerLibrary.Models; using MSURandomizerLibrary.Services; using Serilog; -using TrackerCouncil.Smz3.Data; using TrackerCouncil.Smz3.Shared; namespace TrackerCouncil.Smz3.UI; @@ -51,7 +50,7 @@ public static void Main(string[] args) #endif .CreateLogger(); - Log.Information("Starting SMZ3 Cas' Randomizer {Version}", FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion); + Log.Information("Starting SMZ3 Cas' Randomizer {Version}", App.Version); Log.Information("Config Path: {Directory}", Directories.ConfigPath); Log.Information("Sprite Path: {Directory}", Directories.SpritePath); Log.Information("Tracker Sprite Path: {Directory}", Directories.TrackerSpritePath); @@ -89,16 +88,16 @@ public static void Main(string[] args) catch (Exception e) { Log.Error(e, "[CRASH] Uncaught {Name}: ", e.GetType().Name); - ShowExceptionPopup(e).ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext()); + ShowExceptionPopup(e).ContinueWith(_ => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext()); Dispatcher.UIThread.MainLoop(source.Token); } } // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() + private static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .With(new Win32PlatformOptions() { RenderingMode = new List() { Win32RenderingMode.Software } }) + .With(new Win32PlatformOptions() { RenderingMode = new List { Win32RenderingMode.Software } }) .With(new X11PlatformOptions() { UseDBusFilePicker = false }) .WithInterFont() .LogToTrace() @@ -143,6 +142,23 @@ private static void CopyDefaultFolder() return; } + if (OperatingSystem.IsLinux()) + { + var appImageIdPath = Path.Combine(source, "id.txt"); + var currentIdPath = Path.Combine(Directories.DefaultDataPath, "id.txt"); + + if (File.Exists(appImageIdPath) && File.Exists(currentIdPath)) + { + var appImageId = File.ReadAllText(appImageIdPath); + var currentId = File.ReadAllText(currentIdPath); + if (appImageId == currentId) + { + Log.Information("DefaultData id matches: {Value}", appImageId); + return; + } + } + } + if (Directory.Exists(Directories.DefaultDataPath)) { try diff --git a/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs b/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs index a6c345ff8..4a9048f2f 100644 --- a/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs +++ b/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs @@ -202,12 +202,10 @@ private async Task CheckForUpdates() return; } - var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion; - try { var gitHubRelease = await gitHubReleaseCheckerService - .GetGitHubReleaseToUpdateToAsync("TheTrackerCouncil", "SMZ3Randomizer", version ?? "", false); + .GetGitHubReleaseToUpdateToAsync("TheTrackerCouncil", "SMZ3Randomizer", App.Version, false); if (!string.IsNullOrWhiteSpace(gitHubRelease?.Url) && gitHubRelease.Url != _options.GeneralOptions.IgnoredUpdateUrl) { diff --git a/src/TrackerCouncil.Smz3.UI/Services/MultiplayerConnectWindowService.cs b/src/TrackerCouncil.Smz3.UI/Services/MultiplayerConnectWindowService.cs index 86ab4ed8d..687ad6da7 100644 --- a/src/TrackerCouncil.Smz3.UI/Services/MultiplayerConnectWindowService.cs +++ b/src/TrackerCouncil.Smz3.UI/Services/MultiplayerConnectWindowService.cs @@ -82,13 +82,13 @@ private void MultiplayerClientServiceOnConnected() _options.MultiplayerUrl = _model.Url; _options.Save(); _ = multiplayerClientService.CreateGame(_model.DisplayName, _model.PhoneticName, - _model.MultiplayerGameType, GetVersion(), _model.AsyncGame, _model.SendItemsOnComplete, + _model.MultiplayerGameType, App.Version, _model.AsyncGame, _model.SendItemsOnComplete, _model.DeathLink); } else { logger.LogInformation("Connected to Server successfully. Joining game."); - _ = multiplayerClientService.JoinGame(_model.GameGuid, _model.DisplayName, _model.PhoneticName, GetVersion()); + _ = multiplayerClientService.JoinGame(_model.GameGuid, _model.DisplayName, _model.PhoneticName, App.Version); } } @@ -114,17 +114,6 @@ private void DisplayError(string error) } } - private string GetVersion() - { - var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion ?? ""; - if (version.Contains('+')) - { - version = version[..version.IndexOf('+')]; - } - - return version; - } - public void Dispose() { multiplayerClientService.Connected -= MultiplayerClientServiceOnConnected; diff --git a/src/TrackerCouncil.Smz3.UI/Services/OptionsWindowService.cs b/src/TrackerCouncil.Smz3.UI/Services/OptionsWindowService.cs index d5388a067..e58c0369d 100644 --- a/src/TrackerCouncil.Smz3.UI/Services/OptionsWindowService.cs +++ b/src/TrackerCouncil.Smz3.UI/Services/OptionsWindowService.cs @@ -63,6 +63,12 @@ public OptionsWindowViewModel GetViewModel() _ = UpdateSpritesAsync(); }; + _model.RandomizerOptions.CreateDesktopFileButtonPressed += (_, _) => + { + if (!OperatingSystem.IsLinux()) return; + App.BuildLinuxDesktopFile(); + }; + _model.TrackerOptions.TestTextToSpeechPressed += (_, _) => { communicator.UpdateVolume(_model.TrackerOptions.TextToSpeechVolume); diff --git a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj index 02ea90ef0..44b4e437c 100644 --- a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj +++ b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj @@ -1,45 +1,51 @@ - - WinExe - net8.0 - enable - true - app.manifest - true - 9.9.9 - SMZ3CasRandomizer - Assets\smz3.ico - $(MSBuildProjectName.Replace(" ", "_")) - + + WinExe + net8.0 + enable + true + app.manifest + true + 9.9.10-beta.1 + SMZ3CasRandomizer + Assets\smz3.ico + $(MSBuildProjectName.Replace(" ", "_")) + - - - + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + \ No newline at end of file diff --git a/src/TrackerCouncil.Smz3.UI/ViewModels/SetupWindowViewModel.cs b/src/TrackerCouncil.Smz3.UI/ViewModels/SetupWindowViewModel.cs index d15306e92..bde40a3c1 100644 --- a/src/TrackerCouncil.Smz3.UI/ViewModels/SetupWindowViewModel.cs +++ b/src/TrackerCouncil.Smz3.UI/ViewModels/SetupWindowViewModel.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Media; using AvaloniaControls.Models; @@ -77,6 +78,7 @@ public class SetupWindowViewModel : ViewModelBase public bool TrackerBcuDisabled { get; set; } = true; public bool DisplayPage4 => StepNumber == 4; + public bool DisplayLinuxDesktopButton => OperatingSystem.IsLinux(); } diff --git a/src/TrackerCouncil.Smz3.UI/Views/AboutWindow.axaml.cs b/src/TrackerCouncil.Smz3.UI/Views/AboutWindow.axaml.cs index 769a816cc..31dfadb56 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/AboutWindow.axaml.cs +++ b/src/TrackerCouncil.Smz3.UI/Views/AboutWindow.axaml.cs @@ -10,16 +10,9 @@ public partial class AboutWindow : ScalableWindow { public AboutWindow() { - var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion ?? ""; - - if (version.Contains('+')) - { - version = version[..version.IndexOf('+')]; - } - InitializeComponent(); - TextBlockVersion.Text = $"Version {version}"; + TextBlockVersion.Text = $"Version {App.Version}"; } private void SMZ3Button_OnClick(object? sender, RoutedEventArgs e) diff --git a/src/TrackerCouncil.Smz3.UI/Views/SetupWindow.axaml b/src/TrackerCouncil.Smz3.UI/Views/SetupWindow.axaml index ea00dc31e..40e9bc037 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/SetupWindow.axaml +++ b/src/TrackerCouncil.Smz3.UI/Views/SetupWindow.axaml @@ -202,6 +202,12 @@ + + You can create a .desktop file to add SMZ3 as an application in your desktop environment's menu. + + + + You can open the full settings window to change other settings such as enabling Twitch integration, modifying the tracker UI background, and modifying other tracking behavior. diff --git a/src/TrackerCouncil.Smz3.UI/Views/SetupWindow.axaml.cs b/src/TrackerCouncil.Smz3.UI/Views/SetupWindow.axaml.cs index ef0c7176c..dc3fc937f 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/SetupWindow.axaml.cs +++ b/src/TrackerCouncil.Smz3.UI/Views/SetupWindow.axaml.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; @@ -155,5 +156,11 @@ private void Window_OnClosing(object? sender, WindowClosingEventArgs e) { _service?.OnClose(); } + + private void CreateDesktopFileButton_OnClick(object? sender, RoutedEventArgs e) + { + if (!OperatingSystem.IsLinux()) return; + App.BuildLinuxDesktopFile(); + } } From d565aa49c422e4f4903e32e8f3235cb2baab9197 Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Sun, 12 Oct 2025 21:23:29 -0400 Subject: [PATCH 02/11] Update AppImageDesktopFileCreator version --- src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj index 44b4e437c..d7ca2edf5 100644 --- a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj +++ b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj @@ -21,7 +21,7 @@ below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> - + From 78715eb43e51de1e41236b29177795d61c814d3e Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Thu, 16 Oct 2025 00:02:34 -0400 Subject: [PATCH 03/11] Add popup on startup to create desktop file --- setup/Smz3CasRandomizer.desktop | 2 +- .../Options/GeneralOptions.cs | 2 ++ .../Services/MainWindowService.cs | 20 +++++++++++++++++++ .../ViewModels/MainWindowViewModel.cs | 2 ++ .../Views/MainWindow.axaml.cs | 18 +++++++++++++++++ 5 files changed, 43 insertions(+), 1 deletion(-) diff --git a/setup/Smz3CasRandomizer.desktop b/setup/Smz3CasRandomizer.desktop index e6c19fd40..1a914f1f4 100644 --- a/setup/Smz3CasRandomizer.desktop +++ b/setup/Smz3CasRandomizer.desktop @@ -12,4 +12,4 @@ X-AppImage-Name=${APP_ID}; X-AppImage-Version=${APP_VERSION}; X-AppImage-Arch=${PACKAGE_ARCH}; MimeType= -Keywords= \ No newline at end of file +Keywords=metroid;zelda;smz3;randomizer;tracker \ No newline at end of file diff --git a/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs b/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs index bf95b7e2d..da02bb755 100644 --- a/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs +++ b/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs @@ -88,6 +88,8 @@ public class GeneralOptions : INotifyPropertyChanged public int UndoExpirationTime { get; set; } = 3; public double UIScaleFactor { get; set; } = 1; + public bool SkipDesktopFile { get; set; } + public Dictionary LinkSpriteOptions { get; set; } = new(); public Dictionary SamusSpriteOptions { get; set; } = new(); public Dictionary ShipSpriteOptions { get; set; } = new(); diff --git a/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs b/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs index 4a9048f2f..6aead4165 100644 --- a/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs +++ b/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; +using AppImageDesktopFileCreator; using Avalonia.Threading; using AvaloniaControls.Controls; using AvaloniaControls.ControlServices; @@ -44,6 +45,12 @@ public MainWindowViewModel InitializeModel(MainWindow window) { _options = optionsFactory.Create(); _model.OpenSetupWindow = !_options.GeneralOptions.HasOpenedSetupWindow; + + if (!_model.OpenSetupWindow && OperatingSystem.IsLinux() && !_options.GeneralOptions.SkipDesktopFile) + { + _model.OpenDesktopFileWindow = !DesktopFileCreator.CheckIfDesktopFileExists("org.trackercouncil.smz3"); + } + ITaskService.Run(CheckForUpdates); ITaskService.Run(StartPySpeechService); return _model; @@ -63,6 +70,19 @@ public void IgnoreUpdate() _model.DisplayNewVersionBanner = false; } + public void HandleUserDesktopResponse(bool addDesktopFile) + { + if (addDesktopFile && OperatingSystem.IsLinux()) + { + App.BuildLinuxDesktopFile(); + } + else + { + _options.GeneralOptions.SkipDesktopFile = true; + _options.Save(); + } + } + public async Task ValidateTwitchToken() { if (string.IsNullOrEmpty(_options.GeneralOptions.TwitchOAuthToken)) diff --git a/src/TrackerCouncil.Smz3.UI/ViewModels/MainWindowViewModel.cs b/src/TrackerCouncil.Smz3.UI/ViewModels/MainWindowViewModel.cs index 7fed220cd..65ef0bbba 100644 --- a/src/TrackerCouncil.Smz3.UI/ViewModels/MainWindowViewModel.cs +++ b/src/TrackerCouncil.Smz3.UI/ViewModels/MainWindowViewModel.cs @@ -14,4 +14,6 @@ public MainWindowViewModel() public string NewVersionGitHubUrl { get; set; } = ""; public bool OpenSetupWindow { get; set; } + + public bool OpenDesktopFileWindow { get; set; } } diff --git a/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs b/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs index 79e0fbcf9..34abf81e1 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs +++ b/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs @@ -2,6 +2,7 @@ using System.IO; using System.Threading.Tasks; using Avalonia.Interactivity; +using Avalonia.Threading; using AvaloniaControls; using AvaloniaControls.Controls; using AvaloniaControls.Services; @@ -88,6 +89,15 @@ private async void Control_OnLoaded(object? sender, RoutedEventArgs e) _ = ITaskService.Run(_service.DownloadConfigsAsync); _ = ITaskService.Run(_service.DownloadSpritesAsync); + if (_model.OpenSetupWindow || _model.OpenDesktopFileWindow) + { + _ = Dispatcher.UIThread.InvokeAsync(OpenStartingWindows); + } + } + + private async Task OpenStartingWindows() + { + await Task.Delay(TimeSpan.FromSeconds(0.5)); if (_model.OpenSetupWindow && _serviceProvider != null) { var result = await _serviceProvider.GetRequiredService() @@ -101,6 +111,14 @@ private async void Control_OnLoaded(object? sender, RoutedEventArgs e) _ = SoloRomListPanel.OpenGenerationWindow(); } } + else if (_model.OpenDesktopFileWindow) + { + _model.OpenDesktopFileWindow = false; + var response = await MessageWindow.ShowYesNoDialog( + "Would you like to add SMZ3 to your menu by creating a desktop file?", + "SMZ3 Cas' Randomizer", this); + _service!.HandleUserDesktopResponse(response); + } } private void OptionsMenuItem_OnClick(object? sender, RoutedEventArgs e) From 28c006e73e28ccf41af34af2a6919cbfb32158c1 Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Thu, 16 Oct 2025 00:03:20 -0400 Subject: [PATCH 04/11] Update AppImage library version --- src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj index d7ca2edf5..66064828e 100644 --- a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj +++ b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj @@ -21,7 +21,7 @@ below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> - + From 1660d9585a33e3264a0485a855012eb25636f775 Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Thu, 16 Oct 2025 18:02:16 -0400 Subject: [PATCH 05/11] Update AppImageDesktopFileCreator version --- src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj index 66064828e..ff0d91705 100644 --- a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj +++ b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj @@ -21,7 +21,7 @@ below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> - + From a5fb054dcf24af84e00140b0ede8e8a283c2578c Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Thu, 16 Oct 2025 20:42:26 -0400 Subject: [PATCH 06/11] Update AppImageDesktopFileCreator --- src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs | 2 +- src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs b/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs index 6aead4165..0e8e377ac 100644 --- a/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs +++ b/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs @@ -48,7 +48,7 @@ public MainWindowViewModel InitializeModel(MainWindow window) if (!_model.OpenSetupWindow && OperatingSystem.IsLinux() && !_options.GeneralOptions.SkipDesktopFile) { - _model.OpenDesktopFileWindow = !DesktopFileCreator.CheckIfDesktopFileExists("org.trackercouncil.smz3"); + _model.OpenDesktopFileWindow = !DesktopFileCreator.DoesDesktopFileExist("org.trackercouncil.smz3"); } ITaskService.Run(CheckForUpdates); diff --git a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj index 60b105ca4..1cdcf0bd3 100644 --- a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj +++ b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj @@ -21,7 +21,7 @@ below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> - + From 54c034b20d9545f0aa04c7757f09d3a84fbc639e Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Thu, 16 Oct 2025 21:59:28 -0400 Subject: [PATCH 07/11] Remove warnings --- src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs b/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs index 34abf81e1..c9b0d23ff 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs +++ b/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs @@ -35,17 +35,17 @@ public MainWindow(MainWindowService service, IServiceProvider? serviceProvider, InitializeComponent(); DataContext = _model = _service.InitializeModel(this); - _service.SpriteDownloadStart += (sender, args) => + _service.SpriteDownloadStart += (_, _) => { _spriteDownloadWindow = new SpriteDownloadWindow(); - _spriteDownloadWindow.Closed += (o, eventArgs) => + _spriteDownloadWindow.Closed += (_, _) => { _spriteDownloadWindow = null; }; _spriteDownloadWindow.ShowDialog(this); }; - _service.SpriteDownloadEnd += (sender, args) => _spriteDownloadWindow?.Close(); + _service.SpriteDownloadEnd += (_, _) => _spriteDownloadWindow?.Close(); } public void Reload() @@ -78,7 +78,7 @@ private void DisableUpdatesLink_OnClick(object? sender, RoutedEventArgs e) _service?.DisableUpdates(); } - private async void Control_OnLoaded(object? sender, RoutedEventArgs e) + private void Control_OnLoaded(object? sender, RoutedEventArgs e) { if (_service == null) { From d8dd073703c3df283e1ffd4f5f75b99064c39d04 Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Thu, 16 Oct 2025 23:51:54 -0400 Subject: [PATCH 08/11] Update AppImageDesktopFileCreator --- src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj index 1cdcf0bd3..217c53771 100644 --- a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj +++ b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj @@ -21,7 +21,7 @@ below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> - + From 9528fb143874f9e912aa663d6cbb8c51af27aa20 Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Fri, 17 Oct 2025 15:50:01 -0400 Subject: [PATCH 09/11] Add AppImage updating --- src/TrackerCouncil.Smz3.UI/App.axaml.cs | 7 +++- .../Services/MainWindowService.cs | 14 ++++--- .../TrackerCouncil.Smz3.UI.csproj | 2 +- .../ViewModels/MainWindowViewModel.cs | 2 + .../Views/MainWindow.axaml | 35 +++++++++++------ .../Views/MainWindow.axaml.cs | 39 +++++++++++++++++++ 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/src/TrackerCouncil.Smz3.UI/App.axaml.cs b/src/TrackerCouncil.Smz3.UI/App.axaml.cs index 400c20f22..23d9e20ef 100644 --- a/src/TrackerCouncil.Smz3.UI/App.axaml.cs +++ b/src/TrackerCouncil.Smz3.UI/App.axaml.cs @@ -3,7 +3,7 @@ using System.Reflection; using System.Runtime.Versioning; using System.Threading.Tasks; -using AppImageDesktopFileCreator; +using AppImageManager; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; @@ -17,6 +17,9 @@ namespace TrackerCouncil.Smz3.UI; public partial class App : Application { + public const string AppId = "org.trackercouncil.smz3"; + public const string AppName = "SMZ3 Cas' Randomizer"; + private IGlobalHook? _hook; private Task? _hookRunner; private static readonly string? s_versionOverride = null; @@ -71,7 +74,7 @@ private void NativeMenuItem_OnClick(object? sender, EventArgs e) [SupportedOSPlatform("linux")] internal static CreateDesktopFileResponse BuildLinuxDesktopFile() { - return new DesktopFileBuilder("org.trackercouncil.smz3", "SMZ3 Cas' Randomizer") + return new DesktopFileBuilder(AppId, AppName) .AddUninstallAction(Directories.AppDataFolder) .Build(); } diff --git a/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs b/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs index 0e8e377ac..527cf45d5 100644 --- a/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs +++ b/src/TrackerCouncil.Smz3.UI/Services/MainWindowService.cs @@ -1,10 +1,8 @@ using System; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection; using System.Threading.Tasks; -using AppImageDesktopFileCreator; +using AppImageManager; using Avalonia.Threading; using AvaloniaControls.Controls; using AvaloniaControls.ControlServices; @@ -16,7 +14,6 @@ using PySpeechService.Client; using PySpeechService.TextToSpeech; using TrackerCouncil.Smz3.Chat.Integration; -using TrackerCouncil.Smz3.Data; using TrackerCouncil.Smz3.Data.Configuration; using TrackerCouncil.Smz3.Data.Options; using TrackerCouncil.Smz3.Data.Services; @@ -48,7 +45,7 @@ public MainWindowViewModel InitializeModel(MainWindow window) if (!_model.OpenSetupWindow && OperatingSystem.IsLinux() && !_options.GeneralOptions.SkipDesktopFile) { - _model.OpenDesktopFileWindow = !DesktopFileCreator.DoesDesktopFileExist("org.trackercouncil.smz3"); + _model.OpenDesktopFileWindow = !AppImage.DoesDesktopFileExist(App.AppId); } ITaskService.Run(CheckForUpdates); @@ -231,6 +228,13 @@ private async Task CheckForUpdates() { _model.DisplayNewVersionBanner = true; _model.NewVersionGitHubUrl = gitHubRelease.Url; + + if (OperatingSystem.IsLinux()) + { + _model.NewVersionDownloadUrl = gitHubRelease.Asset + .FirstOrDefault(x => x.Url.ToLower().EndsWith(".appimage"))?.Url; + _model.DisplayDownloadLink = !string.IsNullOrEmpty(_model.NewVersionDownloadUrl); + } } } catch (Exception ex) diff --git a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj index 217c53771..d16a2f7a5 100644 --- a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj +++ b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj @@ -21,7 +21,7 @@ below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> - + diff --git a/src/TrackerCouncil.Smz3.UI/ViewModels/MainWindowViewModel.cs b/src/TrackerCouncil.Smz3.UI/ViewModels/MainWindowViewModel.cs index 65ef0bbba..f147f8f71 100644 --- a/src/TrackerCouncil.Smz3.UI/ViewModels/MainWindowViewModel.cs +++ b/src/TrackerCouncil.Smz3.UI/ViewModels/MainWindowViewModel.cs @@ -10,8 +10,10 @@ public MainWindowViewModel() } [Reactive] public bool DisplayNewVersionBanner { get; set; } + [Reactive] public bool DisplayDownloadLink { get; set; } public string NewVersionGitHubUrl { get; set; } = ""; + public string? NewVersionDownloadUrl { get; set; } public bool OpenSetupWindow { get; set; } diff --git a/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml b/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml index e43bf3fe6..d4b13e21d 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml +++ b/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml @@ -18,7 +18,7 @@ - + @@ -26,21 +26,34 @@ - - - A new version of SMZ3 is now available! - Click here to go to the download page. - + + + A new version of SMZ3 is now available! - + - - Ignore this Version - Don't Check for Updates - + + + + + + + Download Update + + + + + + View On GitHub + + + Ignore this Version + Don't Check for Updates + + diff --git a/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs b/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs index c9b0d23ff..b4a9cdd50 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs +++ b/src/TrackerCouncil.Smz3.UI/Views/MainWindow.axaml.cs @@ -1,6 +1,8 @@ using System; using System.IO; using System.Threading.Tasks; +using AppImageManager; +using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Threading; using AvaloniaControls; @@ -130,4 +132,41 @@ private void AboutButton_OnClick(object? sender, RoutedEventArgs e) { _serviceProvider?.GetRequiredService().ShowDialog(this); } + + private async void DownloadReleaseButton_OnClick(object? sender, RoutedEventArgs e) + { + try + { + if (string.IsNullOrEmpty(_model.NewVersionDownloadUrl)) + { + return; + } + + if (OperatingSystem.IsLinux()) + { + var downloadResult = await AppImage.DownloadAsync(new DownloadAppImageRequest + { + Url = _model.NewVersionDownloadUrl + }); + + if (downloadResult.Success) + { + Close(); + } + else if (downloadResult.DownloadedSuccessfully) + { + await MessageWindow.ShowErrorDialog("AppImage was downloaded, but it could not be launched."); + } + else + { + await MessageWindow.ShowErrorDialog("Failed downloading AppImage"); + } + } + } + catch (Exception exception) + { + Console.WriteLine(exception); + throw; + } + } } From cef77c672d2b8ce9a64a36f85e3d88221f0197a7 Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Fri, 17 Oct 2025 22:29:19 -0400 Subject: [PATCH 10/11] Update linux github action to only run on push --- .github/workflows/dotnet.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 8e84d9011..98e342e21 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -2,17 +2,15 @@ name: .NET Build + Test + Publish on: push: - branches: [main, linux-app-image] + branches: [main] pull_request: branches: [main] jobs: build-windows: runs-on: windows-2022 - outputs: version-number: ${{ steps.version.outputs.number}} - steps: - uses: actions/checkout@v4 - name: Download config repo @@ -90,12 +88,10 @@ jobs: build-linux: runs-on: ubuntu-22.04 - needs: [build-windows] - permissions: contents: write - + if: ${{ github.event_name != 'pull_request' }} steps: - uses: actions/checkout@v4 - name: Download config repo From ee61894d6eb9ce9048ff711bc942c0b4b4976d16 Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Sat, 18 Oct 2025 18:26:08 -0400 Subject: [PATCH 11/11] Update github action to hopefully show as successful --- .github/workflows/dotnet.yml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 98e342e21..27531e8b3 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -91,23 +91,25 @@ jobs: needs: [build-windows] permissions: contents: write - if: ${{ github.event_name != 'pull_request' }} steps: - uses: actions/checkout@v4 - name: Download config repo uses: actions/checkout@v4 + if: ${{ github.event_name != 'pull_request' }} with: repository: TheTrackerCouncil/SMZ3CasConfigs path: configs ref: main - name: Download sprite repo uses: actions/checkout@v4 + if: ${{ github.event_name != 'pull_request' }} with: repository: TheTrackerCouncil/SMZ3CasSprites path: sprites ref: main - name: Download tracker sprite repo uses: actions/checkout@v4 + if: ${{ github.event_name != 'pull_request' }} with: repository: TheTrackerCouncil/TrackerSprites path: trackersprites @@ -126,9 +128,11 @@ jobs: Remove-Item -LiteralPath "trackersprites/.git" -Force -Recurse - name: Setup .NET uses: actions/setup-dotnet@v4 + if: ${{ github.event_name != 'pull_request' }} with: dotnet-version: 8.0.x - name: Update VersionOverride in source file + if: ${{ github.event_name != 'pull_request' }} run: | pwd VERSION="${{ needs.build-windows.outputs.version-number }}" @@ -139,12 +143,15 @@ jobs: echo "Updated VersionOverride to: ${VERSION}" - name: Install PupNet run: dotnet tool install -g KuiperZone.PupNet + if: ${{ github.event_name != 'pull_request' }} - name: Download AppImageTool + if: ${{ github.event_name != 'pull_request' }} run: | wget -P "$HOME/.local/bin" "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" chmod +x "$HOME/.local/bin/appimagetool-x86_64.AppImage" appimagetool-x86_64.AppImage --version - name: Run PupNet + if: ${{ github.event_name != 'pull_request' }} run: | chmod +x setup/AppImageBundleFiles.sh pupnet setup/AppImage.pupnet.conf --kind appimage -y @@ -157,23 +164,25 @@ jobs: build-mac: runs-on: macos-latest - if: ${{ github.event_name != 'pull_request' }} steps: - uses: actions/checkout@v4 - name: Download config repo uses: actions/checkout@v4 + if: ${{ github.event_name != 'pull_request' }} with: repository: TheTrackerCouncil/SMZ3CasConfigs path: configs ref: main - name: Download sprite repo uses: actions/checkout@v4 + if: ${{ github.event_name != 'pull_request' }} with: repository: TheTrackerCouncil/SMZ3CasSprites path: sprites ref: main - name: Download tracker sprite repo uses: actions/checkout@v4 + if: ${{ github.event_name != 'pull_request' }} with: repository: TheTrackerCouncil/TrackerSprites path: trackersprites @@ -192,16 +201,21 @@ jobs: Remove-Item -LiteralPath "trackersprites/.git" -Force -Recurse - name: Setup .NET uses: actions/setup-dotnet@v4 + if: ${{ github.event_name != 'pull_request' }} with: dotnet-version: 8.0.x - name: Restore dependencies run: dotnet restore src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj + if: ${{ github.event_name != 'pull_request' }} - name: Build run: dotnet build --no-restore -p:PostBuildEvent= src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj + if: ${{ github.event_name != 'pull_request' }} - name: Publish run: dotnet publish -r osx-arm64 --configuration Release -p:UseAppHost=true src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj + if: ${{ github.event_name != 'pull_request' }} - name: Get version number id: version + if: ${{ github.event_name != 'pull_request' }} run: | $version = (Get-Item "src\TrackerCouncil.Smz3.UI\bin\Release\net8.0\osx-arm64\publish\SMZ3CasRandomizer.dll").VersionInfo.ProductVersion $version = $version -replace "\+.*", "" @@ -210,10 +224,12 @@ jobs: Write-Output "number=$version" >> $env:GITHUB_OUTPUT shell: pwsh - name: Prepare packaging script + if: ${{ github.event_name != 'pull_request' }} run: | chmod +x ./setup/package-macos-app.sh ./setup/package-macos-app.sh "${{ steps.version.outputs.number }}" - name: Upload artifact + if: ${{ github.event_name != 'pull_request' }} uses: actions/upload-artifact@v4 with: path: "setup/output/*" @@ -229,18 +245,22 @@ jobs: steps: - uses: actions/download-artifact@v5 + if: ${{ github.event_name != 'pull_request' }} with: name: SMZ3CasRandomizerWindows path: out - uses: actions/download-artifact@v5 + if: ${{ github.event_name != 'pull_request' }} with: name: SMZ3CasRandomizerLinux path: out - uses: actions/download-artifact@v5 + if: ${{ github.event_name != 'pull_request' }} with: name: SMZ3CasRandomizerMacOS path: out - name: Extract some files + if: ${{ github.event_name != 'pull_request' }} run: | ls -alR - name: Upload artifact @@ -250,6 +270,7 @@ jobs: path: "out/*" name: SMZ3CasRandomizer_${{ needs.build-windows.outputs.version-number }} - name: Delete old artifacts + if: ${{ github.event_name != 'pull_request' }} uses: geekyeggo/delete-artifact@v5 with: name: |