From 101ccb749dbe6a619c39ce090a9c7757eec4f1c2 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Sun, 30 Nov 2025 16:01:54 -0600 Subject: [PATCH 1/2] Created Release Workflow --- .github/workflows/create-draft-release.yml | 108 +++++++++++++++ Scripts/build.sh | 125 +++++++++++++++++ Scripts/package.sh | 131 ++++++++++++++++++ ShinRyuModManager-CE/BuildAssets/README | 3 + .../ShinRyuModManager-CE.csproj | 4 + 5 files changed, 371 insertions(+) create mode 100644 .github/workflows/create-draft-release.yml create mode 100755 Scripts/build.sh create mode 100755 Scripts/package.sh create mode 100644 ShinRyuModManager-CE/BuildAssets/README diff --git a/.github/workflows/create-draft-release.yml b/.github/workflows/create-draft-release.yml new file mode 100644 index 0000000..814ac6f --- /dev/null +++ b/.github/workflows/create-draft-release.yml @@ -0,0 +1,108 @@ +name: Prepare for Release +on: + workflow_dispatch: + inputs: + name: + description: "Release name" + required: true + default: "ShinRyuModManager-CE 1.0.0" + tag: + description: "Tag for the release (example: 1.2.3)" + required: true + default: "1.0.0" + updater_version: + description: "Version for RyuUpdater (example: 1.2.3)" + required: true + default: "1.0.0" + +jobs: + release: + runs-on: ubuntu-latest + permissions: write-all + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Checkout AppCast Repo + uses: actions/checkout@v4 + with: + repository: 'TheTrueColonel/SRMM-AppCast' + token: '${{ secrets.REPO_PUSH_TOKEN }}' + path: AppcastRepo + + - name: Install .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 10.0.x + + - name: Install Netsparkle Tool + run: dotnet tool install --global NetSparkleUpdater.Tools.AppCastGenerator + + - name: Add .NET tools to PATH + run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH + + - name: Create Draft Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.event.inputs.tag }} + release_name: ${{ github.event.inputs.name }} + draft: true + prerelease: false + + - name: Run Scripts + run: | + ./Scripts/build.sh + ./Scripts/package.sh -s ${{ github.event.inputs.tag }} -u ${{ github.event.inputs.updater_version }} + env: + SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }} + SPARKLE_PUBLIC_KEY: ${{ secrets.SPARKLE_PUBLIC_KEY }} + + - name: Upload SRMM Release Assets + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + UPLOAD_URL: ${{ steps.create_release.outputs.upload_url }} + run: | + for file in ${{ github.workspace }}/dist/srmm/out/*; do + name=$(basename "$file") + mime=$(file --brief --mime-type "$file") + echo "Uploading $name ($mime)" + curl \ + -X POST \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Content-Type: $mime" \ + --data-binary @"$file" \ + --no-progress-meter \ + "${UPLOAD_URL%%\{*}?name=$name" > /dev/null + done + + - name: Commit Appcasts and Updater + run: | + cd ${{ github.workspace }}/AppcastRepo + branch="update-appcasts-${{ github.run_number }}" + git checkout -b "${branch}" + + git add . + git commit -m "Update Appcasts and Updater for SRMM version ${{ github.event.inputs.tag }} and Updater version ${{ github.event.inputs.updater_version }}" + git push origin "${branch}" + env: + GIT_AUTHOR_NAME: github-actions + GIT_AUTHOR_EMAIL: github-actions@github.com + GIT_COMMITTER_NAME: github-actions + GIT_COMMITTER_EMAIL: github-actions@github.com + + - name: Create Pull Request + run: | + cd ${{ github.workspace }}/AppcastRepo + branch="update-appcasts-${{ github.run_number }}" + + gh pr create \ + --title "Update Appcasts and Updater" \ + --body "Automated update for SRMM version ${{ github.event.inputs.tag }} and Updater version ${{ github.event.inputs.updater_version }}" \ + --head "${branch}" \ + --base main + env: + GITHUB_TOKEN: ${{ secrets.REPO_PUSH_TOKEN }} diff --git a/Scripts/build.sh b/Scripts/build.sh new file mode 100755 index 0000000..7b483c4 --- /dev/null +++ b/Scripts/build.sh @@ -0,0 +1,125 @@ +#!/bin/bash + +set -euo pipefail + +### Variables +SRMM_PROJECT="$GITHUB_WORKSPACE/ShinRyuModManager-CE/ShinRyuModManager-CE.csproj" +UPDATER_PROJECT="$GITHUB_WORKSPACE/RyuUpdater/RyuUpdater.csproj" +SRMM_OUTPUT_DIR="$GITHUB_WORKSPACE/dist/srmm" +UPDATER_OUTPUT_DIR="$GITHUB_WORKSPACE/dist/updater" +TEMP_DIR="$RUNNER_TEMP" +PATTERN=".*\.zip$" + +FRAMEWORK="net10.0" + +DOWNLOAD_REPO="SRMM-Studio/ShinRyuModManager" + +FILES_TO_COPY=( + "dinput8.dll" + "winmm.lj" + "YakuzaParless.asi" +) + +# Declares runtime and known build params +declare -A TARGET_ARGS=( + ["linux"]="linux-x64;--self-contained -p:BuildSuffix=linux" + ["linux-slim"]="linux-x64;--no-self-contained -p:BuildSuffix=linux-slim" + ["windows"]="win-x64;--self-contained -p:BuildSuffix=windows" + ["windows-slim"]="win-x64;--no-self-contained -p:BuildSuffix=windows-slim" +) + +declare -A UPDATER_TARGET_ARGS=( + ["linux"]="linux-x64;--self-contained" + ["windows"]="win-x64;--self-contained" +) + +### Get required files from SRMM release +API_URL="https://api.github.com/repos/${DOWNLOAD_REPO}/releases/latest" + +# Using `jq` to parse json response +ASSET_URL=$(curl -s "${API_URL}" | jq -r \ + --arg pattern "${PATTERN}" ' + .assets[] + | select(.name | test($pattern)) + | .browser_download_url + ') + +if [[ -z "${ASSET_URL}" ]]; then + echo "ERROR: No assets matching pattern '${PATTERN}' found." + exit 1 +fi + +echo "Downloading asset from: ${ASSET_URL}" + +ASSET_FILE="${TEMP_DIR}/release.zip" +curl -L "${ASSET_URL}" -o "${ASSET_FILE}" + +echo "Extracting release to ${TEMP_DIR}/extracted" + +mkdir -p "${TEMP_DIR}/extracted" +unzip -q "${ASSET_FILE}" -d "${TEMP_DIR}/extracted" + +### Build + +# Build SRMM +for TARGET in "${!TARGET_ARGS[@]}"; do + OUT_DIR="${SRMM_OUTPUT_DIR}/${TARGET}" + mkdir -p "${OUT_DIR}" + + # Reads the target's arguments and split them into an array + IFS=";" read -r -a arr <<< "${TARGET_ARGS[${TARGET}]}" + + echo "Buidling SRMM ${TARGET}..." + + dotnet publish "${SRMM_PROJECT}" \ + -c "Release" \ + -r "${arr[0]}" \ + -f "${FRAMEWORK}" \ + -o "${OUT_DIR}" \ + -p:PublishSingleFile=true \ + -p:IncludeNativeLibrariesForSelfExtract=true \ + -p:DebugType=None \ + -p:DebugSymbols=false \ + -p:DebugPortablePdb=false \ + ${arr[1]} + + for FILE in "${FILES_TO_COPY[@]}"; do + SRC="${TEMP_DIR}/extracted/${FILE}" + + if [[ -f "${SRC}" ]]; then + cp "${SRC}" "${OUT_DIR}/${FILE}" + echo " Copied: ${FILE}" + else + echo " Warning: Missing file in realease: ${FILE}" + fi + done + + #TODO: Remove when merged. Ensures Parless can find the exe on Windows + if [[ ${TARGET} =~ "windows" ]]; then + mv "${OUT_DIR}/ShinRyuModManager-CE.exe" "${OUT_DIR}/ShinRyuModManager.exe" + fi +done + +# Build Updater +for TARGET in "${!UPDATER_TARGET_ARGS[@]}"; do + OUT_DIR="${UPDATER_OUTPUT_DIR}/${TARGET}" + mkdir -p "${OUT_DIR}" + + # Reads the target's arguments and split them into an array + IFS=";" read -r -a arr <<< "${TARGET_ARGS[${TARGET}]}" + + echo "Buidling RyuUpdater ${TARGET}..." + + dotnet publish "${UPDATER_PROJECT}" \ + -c "Release" \ + -r "${arr[0]}" \ + -f "${FRAMEWORK}" \ + -o "${OUT_DIR}" \ + -p:PublishSingleFile=true \ + -p:IncludeNativeLibrariesForSelfExtract=true \ + -p:DebugType=None \ + -p:DebugSymbols=false \ + -p:DebugPortablePdb=false \ + -p:PublishTrimmed=true \ + ${arr[1]} +done diff --git a/Scripts/package.sh b/Scripts/package.sh new file mode 100755 index 0000000..d1ba83d --- /dev/null +++ b/Scripts/package.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +set -euo pipefail + +### Check arguments +if ! [[ "${#}" -gt 3 ]]; then + echo "Not enough args! ${#}" + echo "Usage: $0 -s -u " + exit 1 +fi + +while getopts s:u: flag; do + case "${flag}" in + s) SRMM_VERSION="${OPTARG}";; + u) UPDATER_VESION="${OPTARG}";; + *) echo "Usage: $0 -s -u "; exit 1;; + esac +done + +if ! [[ "${SRMM_VERSION}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9_]+)?$ || "${UPDATER_VESION}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9_]+)?$ ]]; then + echo "Incorrect format!" + echo "Usage: $0 -s -u " + exit 1 +fi + +# Strip leading "v"s +if [[ "${SRMM_VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9_]+)?$ ]]; then + SRMM_VERSION=${SRMM_VERSION#v} +fi + +if [[ "${UPDATER_VESION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9_]+)?$ ]]; then + UPDATER_VESION=${UPDATER_VESION#v} +fi + +### Variables +SRMM_BASE_NAME="ShinRyuModManager-CE" +UPDATER_BASE_NAME="RyuUpdater" +DIST_SELECTOR="$GITHUB_WORKSPACE/dist" +SRMM_SELECTOR="${DIST_SELECTOR}/srmm" +UPDATER_SELECTOR="${DIST_SELECTOR}/updater" + +SRMM_OUTPUT_DIR="${SRMM_SELECTOR}/out" +UPDATER_OUTPUT_DIR="${UPDATER_SELECTOR}/out" + +APPCAST_OUTPUT_DIR="${DIST_SELECTOR}/appcast" + +SRMM_URL_BASE="https://github.com/TheTrueColonel/ShinRyuModManager-CE/releases/download/v" +UPDATER_URL_BASE="https://thetruecolonel.github.io/SRMM-AppCast/updater/" + +rm -rf "${SRMM_OUTPUT_DIR}" +rm -rf "${UPDATER_OUTPUT_DIR}" + +readarray -t SRMM_BUILD_DIRS < <(find "${SRMM_SELECTOR}" -mindepth 1 -maxdepth 1 -type d -printf '%f\n') +readarray -t UPDATER_BUILD_DIRS < <(find "${UPDATER_SELECTOR}" -mindepth 1 -maxdepth 1 -type d -printf '%f\n') + +mkdir -p "${SRMM_OUTPUT_DIR}" +mkdir -p "${UPDATER_OUTPUT_DIR}" + +for TARGET in "${SRMM_BUILD_DIRS[@]}"; do + DIR="${SRMM_SELECTOR}/${TARGET}" + OUTPUT_TARGET_STR=$(echo "${TARGET}" | sed -e "s/\b\(.\)/\u\1/g") # Capitalizes each target word: linux-slim -> Linux-Slim + OUTPUT_FILE_BASE="${SRMM_BASE_NAME}-${OUTPUT_TARGET_STR}-${SRMM_VERSION}" + + echo "Compressing ${OUTPUT_FILE_BASE}..." + + 7za a "${SRMM_OUTPUT_DIR}/${OUTPUT_FILE_BASE}.zip" -tzip -bd -y "${DIR}/*" > /dev/null + tar czf "${SRMM_OUTPUT_DIR}/${OUTPUT_FILE_BASE}.tar.gz" --owner=0 --group=0 --numeric-owner -C "${DIR}/" . + + ### Create Appcast + + # Really hate how this is done, but I can't think of anything better + if [[ "${OUTPUT_FILE_BASE}" =~ -Linux- ]]; then + OS_NAME="linux" + EXEC_NAME="${SRMM_BASE_NAME}" + elif [[ "${OUTPUT_FILE_BASE}" =~ -Windows- ]]; then + OS_NAME="windows" + EXEC_NAME="ShinRyuModManager.exe" + fi + + # Create only for .zip, as that's universally available + netsparkle-generate-appcast \ + -a "${APPCAST_OUTPUT_DIR}" \ + --single-file "${SRMM_OUTPUT_DIR}/${OUTPUT_FILE_BASE}.zip" \ + -o "${OS_NAME}" \ + -n "${EXEC_NAME}" \ + --output-file-name "appcast_${TARGET}" \ + --use-ed25519-signature-attribute \ + --human-readable \ + --file-version "${SRMM_VERSION}-${TARGET}" \ + -u "${SRMM_URL_BASE}${SRMM_VERSION}/" > /dev/null +done + +for TARGET in "${UPDATER_BUILD_DIRS[@]}"; do + DIR="${UPDATER_SELECTOR}/${TARGET}" + OUTPUT_TARGET_STR=$(echo "${TARGET}" | sed -e "s/\b\(.\)/\u\1/g") # Capitalizes each target word: linux-slim -> Linux-Slim + OUTPUT_FILE_BASE="${UPDATER_BASE_NAME}-${OUTPUT_TARGET_STR}-Latest" + + echo "Compressing ${OUTPUT_FILE_BASE}..." + + 7za a "${UPDATER_OUTPUT_DIR}/${OUTPUT_FILE_BASE}.zip" -tzip -bd -y "${DIR}/*" > /dev/null + tar czf "${UPDATER_OUTPUT_DIR}/${OUTPUT_FILE_BASE}.tar.gz" --owner=0 --group=0 --numeric-owner -C "${DIR}/" . + + cp -r "${UPDATER_OUTPUT_DIR}/." $GITHUB_WORKSPACE/AppcastRepo/updater/ + + ### Create Appcast + + # Really hate how this is done, but I can't think of anything better + if [[ "${OUTPUT_FILE_BASE}" =~ -Linux- ]]; then + OS_NAME="linux" + EXEC_NAME="${UPDATER_BASE_NAME}" + elif [[ "${OUTPUT_FILE_BASE}" =~ -Windows- ]]; then + OS_NAME="windows" + EXEC_NAME="${UPDATER_BASE_NAME}.exe" + fi + + # Create only for .zip, as that's universally available + netsparkle-generate-appcast \ + -a "${APPCAST_OUTPUT_DIR}" \ + --single-file "${UPDATER_OUTPUT_DIR}/${OUTPUT_FILE_BASE}.zip" \ + -o "${OS_NAME}" \ + -n "${EXEC_NAME}" \ + --output-file-name "appcast_ryuupdater-${TARGET}" \ + --use-ed25519-signature-attribute \ + --human-readable \ + --file-version "${SRMM_VERSION}" \ + -u "${UPDATER_URL_BASE}" > /dev/null +done + +### Copy AppCasts to repo + +cp -r "${APPCAST_OUTPUT_DIR}/." $GITHUB_WORKSPACE/AppcastRepo/releases/ diff --git a/ShinRyuModManager-CE/BuildAssets/README b/ShinRyuModManager-CE/BuildAssets/README new file mode 100644 index 0000000..6ae626f --- /dev/null +++ b/ShinRyuModManager-CE/BuildAssets/README @@ -0,0 +1,3 @@ +The slim version requires .NET 10.0 runtime to be installed on the machine. + +https://dotnet.microsoft.com/en-us/download/dotnet/10.0 diff --git a/ShinRyuModManager-CE/ShinRyuModManager-CE.csproj b/ShinRyuModManager-CE/ShinRyuModManager-CE.csproj index 1239fe1..e8922b6 100644 --- a/ShinRyuModManager-CE/ShinRyuModManager-CE.csproj +++ b/ShinRyuModManager-CE/ShinRyuModManager-CE.csproj @@ -88,4 +88,8 @@ + + + + From dcf6622f2d157b995ab8708f208d74a112b302f2 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Sun, 30 Nov 2025 16:03:41 -0600 Subject: [PATCH 2/2] Fixed secret --- .github/workflows/create-draft-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/create-draft-release.yml b/.github/workflows/create-draft-release.yml index 814ac6f..66e415a 100644 --- a/.github/workflows/create-draft-release.yml +++ b/.github/workflows/create-draft-release.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v4 with: repository: 'TheTrueColonel/SRMM-AppCast' - token: '${{ secrets.REPO_PUSH_TOKEN }}' + token: '${{ secrets.SRMM_APPCAST_TOKEN }}' path: AppcastRepo - name: Install .NET SDK @@ -105,4 +105,4 @@ jobs: --head "${branch}" \ --base main env: - GITHUB_TOKEN: ${{ secrets.REPO_PUSH_TOKEN }} + GITHUB_TOKEN: ${{ secrets.SRMM_APPCAST_TOKEN }}