Skip to content

Redist Update

Redist Update #49

Workflow file for this run

name: "Redist Update"
# Scheduled: fetch the official Rocket (LDM) assemblies from the Unturned dedicated
# server's Extras/Rocket.Unturned and, when the Rocket version changed, open a PR.
# Never commits to master directly — redist-verify validates it, redist-publish
# publishes on merge. Mirrors RocketModFix.Unturned.Redist's flow (1 variant here).
on:
schedule:
- cron: "0 * * * *" # hourly; LDM changes a few times a year, so most runs are a no-op
workflow_dispatch:
inputs:
force:
description: "Open a PR even if the Rocket version is unchanged"
type: boolean
default: false
permissions:
contents: read
concurrency:
group: ldm-redist-update
cancel-in-progress: false
env:
APP_ID: "1110390" # Unturned Dedicated Server
CONTENT_DEPOT: "1110393" # content depot holding Extras/Rocket.Unturned
ROCKET_DIR: "download/Extras/Rocket.Unturned"
NUSPEC: "RocketModFix.LDM.Redist.nuspec"
PR_BRANCH: "redist-update/ldm"
jobs:
update:
name: "Update LDM redist"
runs-on: ubuntu-22.04
permissions:
contents: write # peter-evans needs it to push the branch
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Download DepotDownloader
uses: robinraju/release-downloader@28fc21f50d76778e7023361aa1f863e717d3d56f # v1
with:
repository: SteamRE/DepotDownloader
tag: "DepotDownloader_3.4.0" # Pin its version to avoid breaking changes.
fileName: DepotDownloader-linux-x64.zip
out-file-path: depot_downloader
extract: true
- name: Fetch Rocket assemblies from Steam
run: |
set -euo pipefail
chmod +x depot_downloader/DepotDownloader
printf '%s\n' \
'Extras/Rocket.Unturned/Rocket.API.dll' \
'Extras/Rocket.Unturned/Rocket.Core.dll' \
'Extras/Rocket.Unturned/Rocket.Unturned.dll' \
'Extras/Rocket.Unturned/Rocket.Unturned.module' > rocket_filelist.txt
# Anonymous works for the dedicated-server depots. steamcmd can't fetch a
# file subset, so DepotDownloader -filelist is the right tool here; the DLLs
# are MIT (LDM) with public source. Retry: DepotDownloader can drop mid-run.
ok=false
for i in 1 2 3 4; do
rm -rf download
if depot_downloader/DepotDownloader -app "$APP_ID" -depot "$CONTENT_DEPOT" \
-os linux -dir download -filelist rocket_filelist.txt \
&& [ -f "$ROCKET_DIR/Rocket.Unturned.module" ] \
&& [ -f "$ROCKET_DIR/Rocket.Core.dll" ] \
&& [ -f "$ROCKET_DIR/Rocket.API.dll" ] \
&& [ -f "$ROCKET_DIR/Rocket.Unturned.dll" ]; then
ok=true; break
fi
echo "::warning::DepotDownloader attempt ${i}/4 did not produce the Rocket files (likely a transient Steam drop); retrying after backoff..."
[ "$i" -lt 4 ] && sleep $((i * 15))
done
if [ "$ok" != "true" ]; then
echo "::error::Could not fetch the Rocket assemblies after 4 attempts."
exit 1
fi
- name: Resolve version and decide whether to open a PR
id: decide
env:
FORCE: ${{ github.event.inputs.force }}
run: |
set -euo pipefail
new=$(jq -r '.Version' "$ROCKET_DIR/Rocket.Unturned.module")
if [ -z "$new" ] || [ "$new" = "null" ]; then
echo "::error::Could not read .Version from Rocket.Unturned.module."
exit 1
fi
old=$(sed -n 's:.*<version>\(.*\)</version>.*:\1:p' "$NUSPEC" | head -n1)
echo "On master: ${old:-<none>} | Upstream Rocket: $new"
open=false
if [ "${FORCE:-false}" = "true" ]; then
open=true
elif [ "$old" = "$new" ]; then
echo "Rocket unchanged — no PR."
elif [ "$(printf '%s\n%s\n' "$old" "$new" | sort -V | tail -n1)" = "$new" ]; then
open=true
else
echo "::error::Upstream Rocket version ($new) is older than the one on master ($old); refusing to downgrade."
exit 1
fi
echo "version=$new" >> "$GITHUB_OUTPUT"
echo "open=$open" >> "$GITHUB_OUTPUT"
- name: Stage assemblies, version and hash manifest
if: steps.decide.outputs.open == 'true'
env:
VERSION: ${{ steps.decide.outputs.version }}
run: |
set -euo pipefail
mkdir -p assemblies
rm -f assemblies/*.dll
cp "$ROCKET_DIR"/Rocket.API.dll "$ROCKET_DIR"/Rocket.Core.dll "$ROCKET_DIR"/Rocket.Unturned.dll assemblies/
sed -i "s|<version>[^<]*</version>|<version>${VERSION}</version>|" "$NUSPEC"
# Integrity manifest redist-verify checks before merge.
python3 - <<'PY'
import hashlib, json, glob, os
m = {os.path.basename(f): hashlib.sha256(open(f,'rb').read()).hexdigest()
for f in sorted(glob.glob("assemblies/*.dll"))}
json.dump(m, open("assemblies/manifest.sha256.json","w"), indent=2)
PY
# Created with PAT (not GITHUB_TOKEN) so the PR triggers redist-verify, and so
# the bot author gates auto-merge there. Mirrors the Unturned Redist.
- name: Create Pull Request
if: steps.decide.outputs.open == 'true'
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8
with:
token: ${{ secrets.PAT }}
branch: ${{ env.PR_BRANCH }}
delete-branch: true
base: master
add-paths: |
assemblies/**
${{ env.NUSPEC }}
commit-message: "Update to Rocket ${{ steps.decide.outputs.version }}"
title: "🤖 Update to Rocket ${{ steps.decide.outputs.version }}"
committer: rocketmodfixadmin <rocketmodfixadmin@users.noreply.github.com>
author: rocketmodfixadmin <rocketmodfixadmin@users.noreply.github.com>
body: |
Automated update of the official Rocket (LDM) assemblies to **${{ steps.decide.outputs.version }}**.
Validation (files, SHA-256 hashes, no version downgrade) runs on this PR via `redist-verify`. On merge, `redist-publish` pushes to NuGet + GitHub Packages.
labels: |
automated
redist-update
# On any scheduled-run failure, open (or comment on) a tracking issue.
notify-failure:
name: "Notify on failure"
needs: update
if: failure() && github.event_name == 'schedule'
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Open or update tracking issue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
set -euo pipefail
marker="LDM redist update workflow failed"
body=$(printf '⚠️ The scheduled LDM redist update failed.\n\nRun: %s\n\nA Steam fetch (DepotDownloader) or the PR creation errored. Check the run for the failing step.' "$RUN_URL")
gh label create update-failure --repo "$REPO" --color B60205 --description "Automated LDM redist update failure" --force >/dev/null 2>&1 || true
existing=$(gh issue list --repo "$REPO" --state open --search "in:title \"$marker\"" --json number --jq '.[0].number // empty')
if [ -n "$existing" ]; then
gh issue comment "$existing" --repo "$REPO" --body "$body"
else
gh issue create --repo "$REPO" --title "⚠️ $marker" --body "$body" --label update-failure
fi
workflow-keepalive:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
continue-on-error: true
permissions:
actions: write
steps:
- name: Re-enable this scheduled workflow
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
case "${GITHUB_WORKFLOW_REF:?}" in
"${GITHUB_REPOSITORY:?}"/.github/workflows/*.y*ml@*) ;;
*) echo "::error::Unexpected GITHUB_WORKFLOW_REF: ${GITHUB_WORKFLOW_REF}"; exit 1 ;;
esac
workflow="${GITHUB_WORKFLOW_REF%%@*}"
workflow="${workflow#"${GITHUB_REPOSITORY}"/.github/workflows/}"
gh api -X PUT "repos/${GITHUB_REPOSITORY}/actions/workflows/${workflow}/enable"