mirror of
https://github.com/s0liton/ps5-romm.git
synced 2026-10-28 21:01:55 +01:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0181457b8 | ||
|
|
69cb1c7ce3 | ||
|
|
83ee938528 | ||
|
|
2c7b069290 | ||
|
|
f2c0c9bbd1 | ||
|
|
804dea1e50 | ||
|
|
833307403a |
No files matched your search
@@ -1,133 +0,0 @@
|
||||
# Builds RommPS, the PS5 app, into PPSA76677.zip: the file ProsperoStore
|
||||
# installs. release.yml runs it between building the payloads and publishing,
|
||||
# so a PS5 release can't go out without it (1.3.0 did, back when it was built
|
||||
# by hand, and the store sat on 1.2.1 for a day).
|
||||
#
|
||||
# The PS5 stack is checked out at its pins (tools/ps5-stack.sh) and RADV, the
|
||||
# slow part, is built once per Mesa and SDK revision and cached. The zip ends
|
||||
# up as the artifact "rommps"; this workflow never touches a release.
|
||||
name: RommPS
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
sha:
|
||||
description: "Commit to build"
|
||||
type: string
|
||||
required: true
|
||||
tag:
|
||||
description: "The release, e.g. v1.3.1"
|
||||
type: string
|
||||
required: true
|
||||
payloads:
|
||||
description: "Artifact holding that release's romm-sync.elf"
|
||||
type: string
|
||||
required: true
|
||||
# Test runs only, removed before merging: builds this branch with v1.3.0's
|
||||
# payload, and stops at the artifact.
|
||||
push:
|
||||
branches: [ensure-store-zip-creation]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 300
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PS5_STACK: ${{ github.workspace }}/ps5-stack
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha || github.sha }}
|
||||
path: repo
|
||||
|
||||
- name: Resolve version
|
||||
id: version
|
||||
run: |
|
||||
tag="${{ inputs.tag }}"
|
||||
if [ -z "$tag" ]; then
|
||||
IFS=. read -r major minor patch < <(jq -r .contentVersion repo/platform/ps5/app/rommps/ps5/sce_sys/param.json)
|
||||
tag="v$((10#$major)).$((10#$minor)).$((10#$patch))"
|
||||
fi
|
||||
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
||||
echo "Building RommPS for $tag"
|
||||
|
||||
# RommPS carries the payload and starts it: the release's own romm-sync.elf.
|
||||
- uses: actions/download-artifact@v4
|
||||
if: inputs.payloads
|
||||
with:
|
||||
name: ${{ inputs.payloads }}
|
||||
path: payloads
|
||||
|
||||
- name: The payload
|
||||
env:
|
||||
TAG: ${{ steps.version.outputs.tag }}
|
||||
FROM_RELEASE: ${{ inputs.payloads == '' }}
|
||||
run: |
|
||||
mkdir -p repo/build/ps5
|
||||
if [ "$FROM_RELEASE" = true ]; then
|
||||
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" -p romm-sync.elf -O repo/build/ps5/romm-sync.elf
|
||||
else
|
||||
cp payloads/romm-sync.elf repo/build/ps5/romm-sync.elf
|
||||
fi
|
||||
|
||||
- name: Check out the PS5 stack
|
||||
id: stack
|
||||
run: |
|
||||
repo/tools/ps5-stack.sh "$PS5_STACK"
|
||||
pin() { sed -n "s/^$2=//p" "$PS5_STACK/PS5_Vulkan/tools/$1"; }
|
||||
echo "radv=$(pin build-radv.sh mesa_revision)-$(pin setup-native-dependencies.sh sdk_revision)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
# The image tools/ps5-app-build.sh runs everything in. The cache only
|
||||
# saves time, so failing to write it must not fail the build.
|
||||
- uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: repo/platform/ps5/app/docker
|
||||
tags: rommps-ps5app:1
|
||||
load: true
|
||||
cache-from: type=gha,scope=ps5app
|
||||
cache-to: type=gha,mode=max,scope=ps5app,ignore-error=true
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ps5-stack/PS5_Vulkan/.deps/native/radv-release
|
||||
key: radv-release-${{ steps.stack.outputs.radv }}
|
||||
|
||||
# What ps5/tools/bootstrap.sh does, minus the console: PS5_Vulkan's SDK,
|
||||
# its native tool and libc.prx (make builds those first, then a sample a
|
||||
# title doesn't need), and RADV, which build-radv.sh skips when the
|
||||
# cached archive is already at its pins.
|
||||
- name: Build the PS5 stack
|
||||
run: |
|
||||
repo/tools/ps5-app-build.sh bash -c '
|
||||
set -euo pipefail
|
||||
cd /stack/PS5_Vulkan
|
||||
bash tools/setup-native-dependencies.sh > /dev/null
|
||||
make > build-make.log 2>&1 || true
|
||||
[[ -x build/host/ps5-native-tool && -f runtime/libc.prx ]] || { tail -50 build-make.log; exit 1; }
|
||||
time bash tools/build-radv.sh release'
|
||||
|
||||
- name: Title art
|
||||
run: repo/tools/ps5-app-build.sh bash /repo/platform/ps5/app/tools/title-art.sh
|
||||
|
||||
- name: Build and package RommPS
|
||||
run: repo/tools/package-app.sh "${{ steps.version.outputs.tag }}"
|
||||
|
||||
- name: Logs
|
||||
if: failure()
|
||||
run: |
|
||||
shopt -s globstar nullglob
|
||||
for log in ps5-stack/PS5_Vulkan/build-make.log ps5-stack/PS5_Vulkan/.deps/work/**/*.log repo/platform/ps5/app/rommps/build/*.log; do
|
||||
echo "::group::$log"; tail -80 "$log"; echo "::endgroup::"
|
||||
done
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rommps
|
||||
path: repo/build/release/PPSA76677.zip
|
||||
if-no-files-found: error
|
||||
@@ -1,9 +1,4 @@
|
||||
# Builds the PS5 and PS4 payloads and RommPS (PPSA76677.zip, the file
|
||||
# ProsperoStore installs), then publishes them as one GitHub release. Nothing is
|
||||
# published unless all of it built: 1.3.0 went out without the zip, back when
|
||||
# RommPS was built by hand, and the store sat on 1.2.1 for a day.
|
||||
#
|
||||
# Triggered either by:
|
||||
# Builds the PS5 and PS4 payloads and publishes a GitHub release. Triggered either by:
|
||||
#
|
||||
# - merging a PR into main from a branch named with a version, e.g.
|
||||
# release/v1.0.2 or v1.0.2-some-fix (the tag is created on the merge commit)
|
||||
@@ -14,7 +9,6 @@
|
||||
# rebuilds only the PS4 payload from ref and swaps it into the existing tag's
|
||||
# release: a PS4 fix that doesn't make the PS5 download anything. Those already
|
||||
# on that version reinstall it by hand; the in-app update only offers newer ones.
|
||||
# The PS5's files, PPSA76677.zip included, stay exactly as they are.
|
||||
name: Release
|
||||
|
||||
on:
|
||||
@@ -36,15 +30,12 @@ permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
payloads:
|
||||
release:
|
||||
if: >-
|
||||
github.event_name == 'push' ||
|
||||
(github.event.pull_request.merged &&
|
||||
contains(github.event.pull_request.head.ref, 'v'))
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
tag: ${{ steps.version.outputs.tag }}
|
||||
sha: ${{ steps.version.outputs.sha }}
|
||||
steps:
|
||||
- name: Resolve version
|
||||
id: version
|
||||
@@ -77,18 +68,13 @@ jobs:
|
||||
with:
|
||||
ref: ${{ steps.version.outputs.sha }}
|
||||
|
||||
# The catalog offers RommPS as an update only if its contentVersion goes
|
||||
# up, so the tag has to match it: v1.1.0 is 01.001.000. And RommPS starts
|
||||
# the payload it carries, so it must ask for this one. Both are checked
|
||||
# again when it's built (tools/package-app.sh); here they fail in a
|
||||
# minute rather than after the long build.
|
||||
- name: Check versions and release notes
|
||||
# RommPS (built with tools/release-app.sh, outside this workflow) is
|
||||
# offered as an update by the homebrew catalog only if its contentVersion
|
||||
# goes up, so the tag has to match it: v1.1.0 is 01.001.000.
|
||||
- name: Check RommPS's contentVersion
|
||||
if: steps.version.outputs.tag
|
||||
env:
|
||||
TAG: ${{ steps.version.outputs.tag }}
|
||||
# The notes are written by hand, in docs/releases/<tag>.md: the homebrew
|
||||
# catalog shows them as RommPS's release notes, and drops GitHub's
|
||||
# generated list.
|
||||
run: |
|
||||
IFS=. read -r major minor patch <<< "${TAG#v}"
|
||||
want=$(printf "%02d.%03d.%03d" "$major" "$minor" "$patch")
|
||||
@@ -97,14 +83,6 @@ jobs:
|
||||
echo "::error::RommPS's contentVersion is $have, $TAG needs $want (platform/ps5/app/rommps/ps5/sce_sys/param.json)"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q "kMinPayload = \"${TAG#v}\"" platform/ps5/app/rommps/examples/rommps/kit/rommps_app.hpp; then
|
||||
echo "::error::kMinPayload in rommps_app.hpp isn't ${TAG#v}"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -s "docs/releases/$TAG.md" ]; then
|
||||
echo "::error::docs/releases/$TAG.md is missing; write the release notes before tagging"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
if: steps.version.outputs.tag
|
||||
@@ -162,53 +140,27 @@ jobs:
|
||||
}
|
||||
sign romm-sync.elf manifest
|
||||
sign romm-sync-ps4.elf manifest-ps4
|
||||
cd dist && sha256sum * > SHA256SUMS
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: steps.version.outputs.tag
|
||||
with:
|
||||
name: payloads
|
||||
path: dist/
|
||||
if-no-files-found: error
|
||||
|
||||
app:
|
||||
needs: payloads
|
||||
if: needs.payloads.outputs.tag
|
||||
uses: ./.github/workflows/app.yml
|
||||
with:
|
||||
sha: ${{ needs.payloads.outputs.sha }}
|
||||
tag: ${{ needs.payloads.outputs.tag }}
|
||||
payloads: payloads
|
||||
|
||||
# Only once the payloads and RommPS have both built. gh uploads every file
|
||||
# before the release is published, so nobody sees half of one. It creates
|
||||
# the tag on the target commit too; a tag pushed by GITHUB_TOKEN does not
|
||||
# re-trigger this workflow, so there's no double release.
|
||||
publish:
|
||||
needs: [payloads, app]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ needs.payloads.outputs.tag }}
|
||||
SHA: ${{ needs.payloads.outputs.sha }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.payloads.outputs.sha }}
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
# Creates the tag on the target commit if it doesn't exist yet. A tag pushed
|
||||
# by GITHUB_TOKEN does not re-trigger this workflow, so there's no double release.
|
||||
- name: Publish release
|
||||
if: steps.version.outputs.tag
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ steps.version.outputs.tag }}
|
||||
SHA: ${{ steps.version.outputs.sha }}
|
||||
# The notes are written by hand, in docs/releases/<tag>.md: the homebrew
|
||||
# catalog shows them as RommPS's release notes, and drops GitHub's
|
||||
# generated list.
|
||||
run: |
|
||||
for file in romm-sync.elf manifest.json manifest.sig romm-sync-ps4.elf romm-sync-ps4.pkg \
|
||||
manifest-ps4.json manifest-ps4.sig PPSA76677.zip; do
|
||||
[ -s "dist/$file" ] || { echo "::error::dist/$file is missing"; exit 1; }
|
||||
done
|
||||
(cd dist && sha256sum * > SHA256SUMS)
|
||||
notes="docs/releases/$TAG.md"
|
||||
if [ ! -s "$notes" ]; then
|
||||
echo "::error::$notes is missing; write the release notes before tagging"
|
||||
exit 1
|
||||
fi
|
||||
gh release create "$TAG" dist/* \
|
||||
--target "$SHA" --title "$TAG" --notes-file "docs/releases/$TAG.md"
|
||||
--target "$SHA" --title "$TAG" --notes-file "$notes"
|
||||
|
||||
replace-ps4:
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
# Makes sure the latest release actually reaches ProsperoStore. release.yml
|
||||
# doesn't publish a PS5 release without PPSA76677.zip any more (1.3.0 went out
|
||||
# without one and the store sat on 1.2.1 for a day); this is the backstop for
|
||||
# a release published some other way, and for the store side.
|
||||
#
|
||||
# Every hour it looks at the latest release:
|
||||
#
|
||||
# - no zip at all (published by hand, say)
|
||||
# - the store still lists an older version 12 hours on (the catalog's own
|
||||
# bot proposes new releases three times a day)
|
||||
#
|
||||
# A problem opens an issue (one per release) and fails the run; once it's
|
||||
# fixed, the issue is closed. PS4-only re-releases (release.yml's replace-ps4)
|
||||
# never touch the zip, so they never come up here.
|
||||
name: Store
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "17 * * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
ZIP: PPSA76677.zip
|
||||
TITLE_ID: PPSA76677
|
||||
STORE_API: https://prosperostore.net/api/v1/versions.json
|
||||
steps:
|
||||
- name: Check the latest release
|
||||
run: |
|
||||
minutes_since() { echo $(( ($(date +%s) - $(date -d "$1" +%s)) / 60 )); }
|
||||
failed=0
|
||||
|
||||
# report <tag> <problem, or empty when there's none>
|
||||
report() {
|
||||
local title="$1 isn't on ProsperoStore" open
|
||||
open=$(gh issue list --state open --search "in:title \"$title\"" --json number,title |
|
||||
jq -r --arg t "$title" '.[] | select(.title == $t) | .number')
|
||||
if [[ -z "$2" ]]; then
|
||||
for n in $open; do gh issue close "$n" --comment "Fixed."; done
|
||||
echo "$1: fine"
|
||||
return
|
||||
fi
|
||||
[[ -n "$open" ]] || gh issue create --title "$title" --body "$2"
|
||||
echo "::error::$2"
|
||||
failed=1
|
||||
}
|
||||
|
||||
release=$(gh api "repos/$GH_REPO/releases/latest")
|
||||
tag=$(jq -r .tag_name <<< "$release")
|
||||
problem=
|
||||
if ! jq -e --arg zip "$ZIP" 'any(.assets[]; .name == $zip)' <<< "$release" >/dev/null; then
|
||||
problem="$tag is published without $ZIP, so the store has nothing to pick up. Run \`tools/release-app.sh $tag --upload\` from a checkout of $tag."
|
||||
# The store's API being down isn't our problem; the next run asks again.
|
||||
elif listed=$(curl -fsS --retry 3 "$STORE_API" | jq -r --arg id "$TITLE_ID" '.apps[$id].version // empty'); then
|
||||
if [[ "$listed" != "${tag#v}" ]] && (( $(minutes_since "$(jq -r .published_at <<< "$release")") >= 720 )); then
|
||||
problem="$ZIP is attached to $tag, but the store still lists ${listed:-nothing}. Open the catalog update by hand (docs/release-checklist.md)."
|
||||
fi
|
||||
else
|
||||
echo "::warning::couldn't read $STORE_API"
|
||||
exit "$failed" # no verdict, so its issue stays as it is
|
||||
fi
|
||||
report "$tag" "$problem"
|
||||
|
||||
exit "$failed"
|
||||
@@ -28,12 +28,74 @@ A [RomM](https://romm.app) companion client for jailbroken PS5 consoles (etaHEN
|
||||
- **Easter Eggs**
|
||||
- Check in the Settings, scroll the settings list, see if you find the X. This **hint** won't be here forever.
|
||||
|
||||
## Supported Emulators and Games
|
||||
- RommPS has built-in profiles for all known emulators on the PS5, and will set the correct default game, save/save state, and bios directories for them. Since there is a huge influx of new emulators popping up, I will be doing my very best to keep up with them and add profiles accordingly.
|
||||
- You can also manually set the directories for each emulator/platform via the PS5 app or or the Web UI.
|
||||
## Requirements
|
||||
|
||||
### Note
|
||||
- RommPS relies on your emulator **NOT** being sandboxed. Some emulators require a separate payload that must be launched to be able to access outside of the sandbox, so make sure you've followed their instructions to properly install. Sandboxed emulators will not be able to read games or save files.
|
||||
- A PS5 running etaHEN or onionHEN, or a PS4 running GoldHEN
|
||||
- RomM 5.2.0 or newer (may work on older versions)
|
||||
- Whichever other emulators you want
|
||||
|
||||
## Install
|
||||
|
||||
### PS5
|
||||
|
||||
| ProsperoStore | RecompStore |
|
||||
| :---: | :---: |
|
||||
| <img src="/assets/prospero-store-rommps.png" width="300" alt="ProsperoStore Install QR"> | <img src="/assets/recomp-store-rommps.png" width="300" alt="RecompStore Install QR"> |
|
||||
|
||||
### Manual Install
|
||||
Download `PPSA76677.zip` from the releases page, unzip it and copy the `PPSA76677` folder to `/data/homebrew/` on the console. Launch RommPS and it will deploy the background service, and set itself to autostart with etaHen or onionHen.
|
||||
|
||||
If you'd rather run just the payload, without the app:
|
||||
1. Download the latest `romm-sync.elf` from the releases page.
|
||||
2. Send it to the console (or use whichever favorite tool you wish):
|
||||
```sh
|
||||
nc -w 1 <ps5-ip> 9021 < romm-sync.elf
|
||||
```
|
||||
3. Set it up at `http://<ps5-ip>:8780` from another device.
|
||||
|
||||
### PS4
|
||||
|
||||
> **Experimental.** The PS4 is currently considered experimental
|
||||
Needs GoldHEN 2.4b18.5 or newer, whose payload menu runs ELF payloads.
|
||||
R
|
||||
1. Download the latest `romm-sync-ps4.pkg` from the releases page and install it with your package installer.
|
||||
2. Open **RommPS** from the home screen. It copies its payload, `romm-sync-ps4.elf`, to `/data/payloads/`.
|
||||
3. In GoldHEN Settings, open the payloader, pick `romm-sync-ps4.elf` and run it. Add it to the AutoRun queue there and it starts on every jailbreak.
|
||||
4. Open **RommPS** again: it shows the web UI. It's also at `http://<ps4-ip>:8780` from another device.
|
||||
|
||||
The app only installs the payload and shows the web UI; the payload does the syncing, in the background, whether or not the app is open. Without the package, copy `romm-sync-ps4.elf` to `/data/payloads/` yourself (GoldHEN's FTP server, or `make install-ps4 PS4_HOST=<ps4-ip>`).
|
||||
|
||||
**Note:** Don't send the payload to GoldHEN's network loader on port 9090: GoldHEN 2.4 crashes on ELFs sent there. Make sure the console's date and time are set (GoldHEN can keep them updated), or HTTPS won't work.
|
||||
|
||||
### How it runs
|
||||
|
||||
RommPS starts with your HEN, and setup turns this on for you. Both HENs start `payloads/romm-sync.elf` when `romm-sync.elf.auto_start` sits next to it, and RommPS puts both in every HEN's folder on the console (`/data/etaHEN/payloads` and `/data/OnionHEN/payloads`), so switching HENs keeps working. If a payload manager like pldmgr has its own copy, that copy is kept up to date too. If you build it yourself, `make install PS5_HOST=<ps5-ip>` copies it over FTP (add `PS5_FTP=ftp://<ps5-ip>:1337 PS5_HEN_DIR=/data/OnionHEN` for onionHEN's FTP plugin).
|
||||
|
||||
## Updates
|
||||
|
||||
- On the PS5, just update RommPS: from ProsperoStore (or other homebrew storefronts eventually), or by copying the new zip's folder over the old one. The background service comes with it, and RommPS swaps it in the next time you open it, once nothing is syncing or downloading. An older version never replaces a newer one.
|
||||
- On the PS4, the payload updates itself from Settings.
|
||||
|
||||
## Setup Wizard
|
||||
|
||||
The setup wizard walks you through five steps:
|
||||
|
||||
1. Connecting to your server.
|
||||
2. Approving the console in RomM, by scanning a QR code or entering a code.
|
||||
3. Choosing which emulators to sync.
|
||||
4. Checking what the first sync will do.
|
||||
5. Choosing when to sync in the background.
|
||||
|
||||
**Back up your saves before the first sync.** Copy your emulator save folders to a computer or USB drive. RommPS keeps a local copy before replacing any file, but your own backup is the safest option.
|
||||
|
||||
## Emulators
|
||||
RommPS has built-in profiles for most known emulators on the PS5, and will set the correct default game, save/save state, and bios directories for them. Since there is a huge influx of new emulators popping up, I will be doing my very best to keep up with them and add profiles accordingly.
|
||||
|
||||
Note: RommPS relies on your emulator **NOT** being sandboxed. Some emulators require a separate payload that must be launched to be able to access outside of the sandbox, so make sure you've followed their instructions to properly install. Sandboxed emulators will not be able to read games or save files.
|
||||
|
||||
> The emulators listed below are those I've tested and have created profiles for to auto-detect them. You can also manually set the directories for each emulator/platform via the PS5 app or or the Web UI; you can use whichever emulators you wish.
|
||||
|
||||
### PS5
|
||||
|
||||
| Emulator | Systems | Game Downloads | Saves | Comments |
|
||||
| --- | --- | --- | --- | --- |
|
||||
@@ -47,141 +109,32 @@ A [RomM](https://romm.app) companion client for jailbroken PS5 consoles (etaHEN
|
||||
| [ProsperoEden](https://github.com/blackbearreloaded/ProsperoEden) | Switch | Manual Support | Researching | - |
|
||||
| [PS5X360](https://github.com/BrinooTk/PS5X360) | Xbox 360 | Manual Support | Researching | - |
|
||||
|
||||
To give some meaning to the table above. **Full support** means RommPS can detect and properly set game/save directories for that emulator. **Manual Support** means you can use it to sync games and/or saves, but you'll need to point it to the directories yourself. This typically means that full support is coming shortly. Broken or N/A means there was a problem keeping us from sync games and saves, or the emulator just fall out doesn't work or crashes. **Researching** means I am seeing what is possible when it comes to that sync type for the given emulator or game system.
|
||||
|
||||
### Important: Save States
|
||||
Some users have recently reported issues with being able to pull down save states from their servers. They don't see them on the destination save folder for that emulator, or if they do, they don't work. Save states typically cannot be used across different emulators (cores), and even within the same core, the definition of a save state may different with file version, and version of the app.
|
||||
|
||||
**Simply put**:
|
||||
- Emulator core must be the same.
|
||||
- Different schema versions of save states are sometimes incompatible with each other.
|
||||
|
||||
For the 1.2.0 release I will be including some fixes as well as some conversion logic to try and convert save state formats for max compatibility whenever possible. This is also why we have an upload only or upload and download option for save states. Maybe you don't care about syncing, but want to back them up, in case something happens. That's a good use case. I want to be blunt with you: Save states are kind of useless. Yes, they're great for recovering your progress after you feel asleep and forgot to save, but you truly should be saving progress with the in-game saves, not save status.
|
||||
|
||||
### PS4 (experimental) ###
|
||||
### PS4
|
||||
|
||||
| Emulator | Systems |
|
||||
| --- | --- |
|
||||
| RetroArch | The RetroArch systems above, wherever the PS4 build has the core |
|
||||
| pSNES, pNES, pGEN, pGBA, pFBN | SNES; NES; Genesis, Master System, Game Gear, Sega CD, SG-1000; GBA, GBC, GB; arcade |
|
||||
|
||||
Only RetroArch has been tested on the PS4 so far.
|
||||
|
||||
### Save states
|
||||
Save states only load in the emulator core that made them, sometimes only in the same version of it. RommPS backs every state up to RomM, and with **Save states: Back up and restore** in Settings it brings one down only to an emulator that can load it:
|
||||
- **RetroArch:** states from the same core, from RomM's web player or another RetroArch.
|
||||
- **PSXS5:** PCSX-ReARMed or Beetle states, whichever core PSXS5 is set to. They're converted to and from the web player's format.
|
||||
- **SwanStationPS5, PS5N64:** only their own states.
|
||||
|
||||
A state from elsewhere never replaces one of yours: it goes into the next free slot.
|
||||
Some users have recently reported issues with being able to pull down save states from their servers. They don't see them on the destination save folder for that emulator, or if they do, they don't work. Save states typically cannot be used across different emulators (cores), and even within the same core, the definition of a save state may different with the file version, and version of the app. In my opinion, save states should be for quick resume or to save you when you forgot to save your game, so they truly don't need to be synced across devices.
|
||||
|
||||
## PS4/PS5 Games
|
||||
|
||||
If you are interested in downloading PS4 and PS5 games from your RomM server, RomM has direct FPKGi support, simply add it as a library. This sync client does NOT handle PS4/PS5 save syncing at this time, but is being researched for potential future inclusion.
|
||||
|
||||
## Requirements
|
||||
|
||||
- A PS5 running etaHEN or onionHEN, or a PS4 running GoldHEN
|
||||
- RomM 5.2.0 or newer (5.3.0 if you want to do shared memory card syncing instead of per-game)
|
||||
- RetroArch installed
|
||||
- Whichever other emulators you want
|
||||
|
||||
## Web UI
|
||||
- RommPS exposes a separate Web UI at http://your-ps5-ip:8780 which allows you to access most of the features present in the web app, but can make things like sync profiled editing, and adding multiple downloads of games easier from your computer instead.
|
||||
|
||||
## Install
|
||||
|
||||
### Install via ProsperoStore
|
||||
|
||||

|
||||
|
||||
Install **RommPS** from [ProsperoStore](https://homebrew.page) and open it. It'll set up the background daemon, and take you through a setup wizard. That's it: RommPS carries the RomM Sync payload and starts it for you. From then on it starts with your HEN, so after a jailbreak or waking the console it's ready to go.
|
||||
|
||||
Not using the store? Download `PPSA76677.zip` from the releases page, unzip it and copy the `PPSA76677` folder to `/data/homebrew/` on the console.
|
||||
|
||||
If you'd rather run just the payload, without the app:
|
||||
|
||||
1. Download the latest `romm-sync.elf` from the releases page.
|
||||
2. Send it to the console (or use whichever favorite tool you wish):
|
||||
```sh
|
||||
nc -w 1 <ps5-ip> 9021 < romm-sync.elf
|
||||
```
|
||||
3. Set it up at `http://<ps5-ip>:8780` from another device.
|
||||
|
||||
On the PS5 the payload doesn't update itself: new versions come with RommPS. Without the app, download the new `romm-sync.elf` yourself.
|
||||
|
||||
RomM Sync starts with your HEN, and setup turns this on for you. Both HENs start `payloads/romm-sync.elf` when `romm-sync.elf.auto_start` sits next to it, and RomM Sync puts both in every HEN's folder on the console (`/data/etaHEN/payloads` and `/data/OnionHEN/payloads`), so switching HENs keeps working. If a payload manager like pldmgr has its own copy, that copy is kept up to date too. If you build it yourself, `make install PS5_HOST=<ps5-ip>` copies it over FTP (add `PS5_FTP=ftp://<ps5-ip>:1337 PS5_HEN_DIR=/data/OnionHEN` for onionHEN's FTP plugin).
|
||||
|
||||
### PS4
|
||||
|
||||
> **Experimental.** The PS4 build has only been tested with RetroArch so far. Standalone emulators may work, but haven't been confirmed. I spent all my focus on the PS5 build, so I didn't get to test as much, so consider it unstable and there may be gremlins for now.
|
||||
|
||||
Needs GoldHEN 2.4b18.5 or newer, whose payload menu runs ELF payloads.
|
||||
|
||||
1. Download the latest `romm-sync-ps4.pkg` from the releases page and install it with your package installer.
|
||||
2. Open **RomM Sync** from the home screen. It copies its payload, `romm-sync-ps4.elf`, to `/data/payloads/`.
|
||||
3. In GoldHEN Settings, open the payloader, pick `romm-sync-ps4.elf` and run it. Add it to the AutoRun queue there and it starts on every jailbreak.
|
||||
4. Open **RomM Sync** again: it shows the web UI. It's also at `http://<ps4-ip>:8780` from another device.
|
||||
|
||||
The app only installs the payload and shows the web UI; the payload does the syncing, in the background, whether or not the app is open. Without the package, copy `romm-sync-ps4.elf` to `/data/payloads/` yourself (GoldHEN's FTP server, or `make install-ps4 PS4_HOST=<ps4-ip>`).
|
||||
|
||||
Don't send the payload to GoldHEN's network loader on port 9090: GoldHEN 2.4 crashes on ELFs sent there. Make sure the console's date and time are set (GoldHEN can keep them updated), or HTTPS won't work. RetroArch is found in `/data/retroarch`, with its cores in `/data/self/retroarch/cores`, as its `retroarch.cfg` says.
|
||||
|
||||
## Updates
|
||||
|
||||
On the PS5, just update RommPS: from ProsperoStore (or other homebrew storefronts eventually), or by copying the new zip's folder over the old one. The background service comes with it, and RommPS swaps it in the next time you open it, once nothing is syncing or downloading. An older version never replaces a newer one.
|
||||
|
||||
On the PS4, the payload updates itself from Settings.
|
||||
|
||||
## Setup
|
||||
|
||||
The setup walks you through five steps:
|
||||
|
||||
1. Connecting to your server.
|
||||
2. Approving the console in RomM, by scanning a QR code or entering a code.
|
||||
3. Choosing which emulators to sync.
|
||||
4. Checking what the first sync will do.
|
||||
5. Choosing when to sync in the background.
|
||||
|
||||
**Back up your saves before the first sync.** Copy your emulator save folders to a computer or USB drive. RomM Sync keeps a local copy before replacing any file, but your own backup is the safest option.
|
||||
|
||||
## PS2 Memory Cards
|
||||
|
||||
LRPS2 keeps every game on one shared memory card by default. RomM stores saves per game, so pick one of these during setup:
|
||||
- **One card per game.** Works with any RomM version. In RetroArch, open the LRPS2 core options and turn off _Shared Memory Cards_. After that, each game's card syncs like any other save. Saves already on the shared card stay there.
|
||||
- **Back up the shared card.** Needs RomM 5.3.0 or newer. The whole card is uploaded when it changes, and you can restore it from Settings in the web UI. It isn't merged with other devices.
|
||||
|
||||
PS5SX2 used 2 shared memory cards in slots 1 and 2. While it does support per-game memory cards, they need to be specified in a per-game ini file. In 1.2.0, I plan on adding the ability to directly modify those .ini files in PS5SX2 to write the proper per-game memory card path, effectively making RommPS handle per-game memory cards for it gracefully. Until then, I suggest using whichever emulator fits your preferred method (PS5SX2 for shared mem cards or use LRPS2 in retroarch for per-game support). If you have both installed, RommPS will sync game and saves, but they will be separate and different saves (PS5SX2 will download your shared memory cards, LRPS2 will grab the memory card per game). I know...it's confusing, but this is just a side-effect of the boom in emulators being released.
|
||||
|
||||
## Other "Memory Card" Consoles
|
||||
PSP, GameCube and Wii saves are synced as one zip per game, the same format other RomM clients use. Depending on the client, they may or may not support unzipping the saves (I don't truly know), but this client does!
|
||||
|
||||
My opinion? One card per game is better since 8MB memory cards fill up quick.
|
||||
RommPS exposes a separate Web UI at http://your-ps5-ip:8780 where you can access all the same features present in the native app.
|
||||
|
||||
## Emulator Profiles
|
||||
|
||||
I've included direct support for most of the emulators that are out right now, except for the ones that won't even launch... you can sync games, states, and saves anywhere you want, but the presets should do it for most people.
|
||||
|
||||
Settings in the web UI has a JSON editor for emulator profiles, and a table showing which folders are used for each platform. A profile lists an emulator's folders and the RomM platforms it plays:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "retroarch",
|
||||
"name": "RetroArch",
|
||||
"root": "/data/homebrew/PPSA99169",
|
||||
"retroarch_cfg": "{root}/config/retroarch.cfg",
|
||||
"rom_dir": "{root}/content/{platform}",
|
||||
"save_dir": "{root}/savefiles",
|
||||
"state_dir": "{root}/savestates",
|
||||
"bios_dir": "{root}/system",
|
||||
"save_exts": ".srm,.sav",
|
||||
"platforms": [{ "romm": ["snes", "sfam"], "dir": "snes", "emulator": "snes9x", "core_name": "Snes9x" }]
|
||||
}
|
||||
```
|
||||
|
||||
I've included direct support for most of the emulators that are out right now, except for the ones that won't even launch... you can sync games, states, and saves anywhere you want, but the presets should do it for most people. You can also configure the paths for all platforms manually. The web UI has a JSON editor for your emulator profiles.
|
||||
|
||||
## Building
|
||||
|
||||
Both consoles build from the same code in `src/`. What differs lives in `platform/<console>/`: `platform.c` (the `src/platform.h` interface), the make rules and the Docker SDK image. `platform/console/` has the parts the PS4 and PS5 share, and `platform/host/` is the development build.
|
||||
|
||||
```sh
|
||||
make host tools # macOS or Linux build for development, plus tools/release-sign
|
||||
tools/ps5-build.sh # PS5 build in Docker, output in build/ps5/
|
||||
@@ -191,7 +144,11 @@ tests/e2e.sh # end-to-end tests against a mock RomM server
|
||||
tests/autostart.sh # autostart, version checks and HEN folders
|
||||
```
|
||||
|
||||
Both consoles build from the same code in `src/`. What differs lives in `platform/<console>/`: `platform.c` (the `src/platform.h` interface), the make rules and the Docker SDK image. `platform/console/` has the parts the PS4 and PS5 share, and `platform/host/` is the development build.
|
||||
## Watch Development Progress
|
||||
|
||||
I've started streaming the development of this app as well as other side projects on my YouTube channel. Nothing serious, but you can catch me live streaming. ;)
|
||||
|
||||
[My Channel](https://www.youtube.com/@soliton_radar)
|
||||
|
||||
## P.S to the industry
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -45,9 +45,5 @@ For each one, start from the old version actually installed and set up, then upd
|
||||
|
||||
## After
|
||||
|
||||
Every PS5 release, alone or with the PS4, ships `PPSA76677.zip`. Only a PS4-only re-release (the manual Release run) skips it, and that never touches the PS5's files.
|
||||
|
||||
- The release workflow builds the payloads, then RommPS (`.github/workflows/app.yml`), and publishes the release only once both have built. No zip, no release: rerun the failed job.
|
||||
- The store: the catalog bot proposes the new version within 8 hours. For sooner, open the pull request yourself on the catalog (`apps/PPSA76677.json`: `version`, `artifact_url`, `sha256`, `icon_url`), from the account that owns the repository; it's merged and live within minutes.
|
||||
- The Store workflow checks every hour and opens an issue if the latest release has no zip, or the store still lists the old version 12 hours after publishing.
|
||||
- A release that went out without the zip anyway: `git checkout <tag> && tools/release-app.sh <tag> --upload` builds it here and attaches it.
|
||||
- The release workflow published the payloads, then `tools/release-app.sh <tag> --upload` attached `PPSA76677.zip`.
|
||||
- The catalog bot's pull request for the new version shows up the next day.
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds RommPS from scratch and packages it as build/release/PPSA76677.zip,
|
||||
# the file ProsperoStore installs.
|
||||
#
|
||||
# tools/package-app.sh v1.3.1
|
||||
#
|
||||
# Used by the release workflow (.github/workflows/app.yml) and by
|
||||
# tools/release-app.sh. It expects build/ps5/romm-sync.elf to be the payload
|
||||
# of that same release, since RommPS carries it and starts it, and the title
|
||||
# art (pic0/pic1.dds, from platform/ps5/app/tools/title-art.sh) in place.
|
||||
# The PS5 stack is PS5_STACK, or ../ps5-stack (tools/ps5-app-build.sh).
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
APP="$ROOT/platform/ps5/app/rommps"
|
||||
TAG="${1:?usage: tools/package-app.sh vX.Y.Z}"
|
||||
die() { echo "package-app: $*" >&2; exit 1; }
|
||||
|
||||
# The catalog tells consoles about an update from contentVersion, so it must
|
||||
# match the tag: v1.1.0 is 01.001.000.
|
||||
[[ "$TAG" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]] || die "$TAG isn't a vX.Y.Z tag"
|
||||
want=$(printf "%02d.%03d.%03d" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}")
|
||||
have=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["contentVersion"])' "$APP/ps5/sce_sys/param.json")
|
||||
[[ "$have" == "$want" ]] || die "contentVersion is $have, $TAG needs $want (ps5/sce_sys/param.json)"
|
||||
grep -q "kMinPayload = \"${TAG#v}\"" "$APP/examples/rommps/kit/rommps_app.hpp" ||
|
||||
die "kMinPayload in rommps_app.hpp isn't ${TAG#v}"
|
||||
for art in pic0 pic1; do
|
||||
[[ -s "$APP/ps5/sce_sys/$art.dds" ]] || die "$art.dds is missing: run platform/ps5/app/tools/title-art.sh"
|
||||
done
|
||||
[[ -s "$ROOT/build/ps5/romm-sync.elf" ]] || die "build/ps5/romm-sync.elf is missing"
|
||||
|
||||
# From scratch: a dev build left behind gives ninja nothing to do, and its
|
||||
# notices still say "uncommitted changes", so the check below refuses it.
|
||||
rm -rf "$APP/build/ps5" "$APP/dist/PPSA76677"
|
||||
"$ROOT/tools/ps5-app-build.sh" bash -c 'cd /repo/platform/ps5/app/rommps && bash ps5/tools/build.sh rommps'
|
||||
# The notices name the commit of every part; one built with uncommitted changes
|
||||
# wouldn't be the source they point to.
|
||||
dirty=$(python3 -c 'import json,sys; print(" ".join(p["id"] for p in json.load(open(sys.argv[1])) if p["source"].get("dirty")))' \
|
||||
"$APP/dist/PPSA76677/licenses/components.json")
|
||||
[[ -z "$dirty" ]] || die "built from uncommitted changes in: $dirty"
|
||||
cmp -s "$ROOT/build/ps5/romm-sync.elf" "$APP/dist/PPSA76677/assets/rommps/romm-sync.elf" ||
|
||||
die "the romm-sync.elf in RommPS isn't build/ps5/romm-sync.elf"
|
||||
|
||||
mkdir -p "$ROOT/build/release"
|
||||
rm -f "$ROOT/build/release/PPSA76677.zip"
|
||||
(cd "$APP/dist" && zip -qrX "$ROOT/build/release/PPSA76677.zip" PPSA76677 -x 'PPSA76677/screenshots/*')
|
||||
(cd "$ROOT" && shasum -a 256 build/release/PPSA76677.zip)
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Checks out the PS5 stack RommPS builds against, at the commits a release
|
||||
# uses, for the release workflow (or anyone without ../ps5-stack):
|
||||
#
|
||||
# tools/ps5-stack.sh <dir> PS5_Vulkan, PS5_Mesa and PS5_PayloadSDK in <dir>
|
||||
#
|
||||
# PS5_Vulkan is pinned here. Everything else is pinned where it's used, and
|
||||
# read from there so there's one place to bump: Mesa by PS5_Vulkan's
|
||||
# tools/build-radv.sh, the payload SDK by PS5_Vulkan's
|
||||
# tools/setup-native-dependencies.sh and by RommPS's ps5/tools/setup-sdk.sh.
|
||||
# Every script that uses them does `git archive <pin>`, so only those commits
|
||||
# are fetched, not the histories (Mesa's alone is a gigabyte).
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
STACK="${1:?usage: tools/ps5-stack.sh <dir>}"
|
||||
|
||||
# 5b5e4fc: what 1.3.0 was built with. RADV comes out of this one's pin of PS5_Mesa.
|
||||
vulkan_revision=5b5e4fc2d80fdb67a4f61ba9e6a8a424026bb8a5
|
||||
|
||||
# fetch <repo> <commit>...: a repository holding just those commits, the first checked out
|
||||
fetch() {
|
||||
local repo="$1" dir="$STACK/$1"
|
||||
shift
|
||||
[[ -d $dir/.git ]] || git init -q "$dir"
|
||||
git -C "$dir" remote get-url origin >/dev/null 2>&1 ||
|
||||
git -C "$dir" remote add origin "https://github.com/mihawk-99/$repo.git"
|
||||
git -C "$dir" fetch -q --depth 1 origin "$@"
|
||||
git -C "$dir" -c advice.detachedHead=false checkout -q "$1"
|
||||
echo "$repo at ${1:0:11}"
|
||||
}
|
||||
pin() { sed -n "s/^$2=//p" "$1"; }
|
||||
|
||||
fetch PS5_Vulkan "$vulkan_revision"
|
||||
fetch PS5_Mesa "$(pin "$STACK/PS5_Vulkan/tools/build-radv.sh" mesa_revision)"
|
||||
fetch PS5_PayloadSDK "$(pin "$ROOT/platform/ps5/app/rommps/ps5/tools/setup-sdk.sh" sdk_revision)" \
|
||||
"$(pin "$STACK/PS5_Vulkan/tools/setup-native-dependencies.sh" sdk_revision)"
|
||||
+31
-15
@@ -1,40 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds RommPS for a release here, and attaches PPSA76677.zip to its GitHub release.
|
||||
# Builds RommPS for a release and attaches PPSA76677.zip to its GitHub release.
|
||||
#
|
||||
# tools/release-app.sh v1.1.0 build and package it, in build/release/
|
||||
# tools/release-app.sh v1.1.0 --upload and attach it to the release v1.1.0
|
||||
#
|
||||
# The release workflow builds and attaches the zip itself (.github/workflows/app.yml);
|
||||
# a PS5 release isn't published without it. This is the fallback for a release
|
||||
# that went out without one anyway, which is how the store sat on 1.2.1 for a
|
||||
# day after 1.3.0. The homebrew catalog picks the release up from the asset's
|
||||
# name, so it stays PPSA76677.zip, and an asset already published is never replaced.
|
||||
# The release workflow can't build RommPS: it needs the PS5 stack beside this
|
||||
# repository (tools/ps5-app-build.sh). So it is built here, from the tagged
|
||||
# commit, once the workflow has published the payloads. The homebrew catalog picks the release up from the asset's name,
|
||||
# so it stays PPSA76677.zip, and an asset already published is never replaced.
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
APP="$ROOT/platform/ps5/app/rommps"
|
||||
TAG="${1:?usage: tools/release-app.sh vX.Y.Z [--upload]}"
|
||||
die() { echo "release-app: $*" >&2; exit 1; }
|
||||
|
||||
# The catalog tells consoles about an update from contentVersion, so it must
|
||||
# match the tag: v1.1.0 is 01.001.000.
|
||||
[[ "$TAG" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]] || die "$TAG isn't a vX.Y.Z tag"
|
||||
want=$(printf "%02d.%03d.%03d" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}")
|
||||
have=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["contentVersion"])' "$APP/ps5/sce_sys/param.json")
|
||||
[[ "$have" == "$want" ]] || die "contentVersion is $have, $TAG needs $want (ps5/sce_sys/param.json)"
|
||||
|
||||
cd "$ROOT"
|
||||
git diff --quiet && git diff --cached --quiet || die "commit or stash your changes first"
|
||||
[[ "$(git rev-parse HEAD)" == "$(git rev-parse "$TAG^{commit}" 2>/dev/null)" ]] || die "check out $TAG first"
|
||||
if [[ "${2:-}" == --upload ]] && gh release view "$TAG" --json assets -q '.assets[].name' | grep -qx PPSA76677.zip; then
|
||||
die "$TAG already has PPSA76677.zip; publish a new release instead of replacing it"
|
||||
fi
|
||||
for art in pic0 pic1; do # the home screen art, made by platform/ps5/app/tools/title-art.sh
|
||||
[[ -s "$APP/ps5/sce_sys/$art.dds" ]] || die "$art.dds is missing: run platform/ps5/app/tools/title-art.sh"
|
||||
done
|
||||
|
||||
# RommPS carries the payload and starts it when it isn't running: the
|
||||
# release's own romm-sync.elf, the same file the in-app updater installs.
|
||||
grep -q "kMinPayload = \"${TAG#v}\"" "$APP/examples/rommps/kit/rommps_app.hpp" ||
|
||||
die "kMinPayload in rommps_app.hpp isn't ${TAG#v}"
|
||||
mkdir -p build/ps5
|
||||
gh release download "$TAG" -p romm-sync.elf -O build/ps5/romm-sync.elf --clobber
|
||||
(cd build/ps5 && gh release download "$TAG" -p SHA256SUMS -O - | grep " romm-sync.elf$" | shasum -a 256 -c -) ||
|
||||
die "build/ps5/romm-sync.elf isn't $TAG's"
|
||||
|
||||
tools/package-app.sh "$TAG"
|
||||
tools/ps5-app-build.sh bash -c 'cd /repo/platform/ps5/app/rommps && bash ps5/tools/build.sh rommps'
|
||||
# The notices name the commit of every part; one built with uncommitted changes
|
||||
# wouldn't be the source they point to.
|
||||
dirty=$(python3 -c 'import json,sys; print(" ".join(p["id"] for p in json.load(open(sys.argv[1])) if p["source"].get("dirty")))' \
|
||||
"$APP/dist/PPSA76677/licenses/components.json")
|
||||
[[ -z "$dirty" ]] || die "built from uncommitted changes in: $dirty"
|
||||
|
||||
mkdir -p build/release
|
||||
rm -f build/release/PPSA76677.zip
|
||||
(cd "$APP/dist" && zip -qrX "$ROOT/build/release/PPSA76677.zip" PPSA76677 -x 'PPSA76677/screenshots/*')
|
||||
shasum -a 256 build/release/PPSA76677.zip
|
||||
|
||||
if [[ "${2:-}" == --upload ]]; then
|
||||
gh release view "$TAG" --json assets -q '.assets[].name' | grep -qx PPSA76677.zip &&
|
||||
die "$TAG already has PPSA76677.zip; publish a new release instead of replacing it"
|
||||
gh release upload "$TAG" build/release/PPSA76677.zip
|
||||
local_sum=$(shasum -a 256 build/release/PPSA76677.zip | cut -d' ' -f1)
|
||||
remote_sum=$(gh release download "$TAG" -p PPSA76677.zip -O - | shasum -a 256 | cut -d' ' -f1)
|
||||
[[ "$local_sum" == "$remote_sum" ]] || die "the PPSA76677.zip on $TAG isn't the one built here"
|
||||
echo "$TAG has PPSA76677.zip ($local_sum). The catalog bot proposes it within 8 hours;"
|
||||
echo "for sooner, open the update yourself (docs/release-checklist.md)."
|
||||
fi
|
||||
Reference in new issue
Block a user