feat(ci): add release workflow + update readme

This commit is contained in:
boykopovar 2026-09-29 00:15:21 +03:00
parent 00261033fa
commit 1071c9e09c
5 changed files with 190 additions and 13 deletions

111
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,111 @@
name: Release
on:
push:
tags: ['v*']
permissions:
contents: read
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
env:
CONFIGURE: >-
-G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON
-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
WINLIBS_RELEASE: 15.2.0posix-14.0.0-ucrt-r7
WINLIBS_ZIP: winlibs-x86_64-posix-seh-gcc-15.2.0-mingw-w64ucrt-14.0.0-r7.zip
jobs:
build:
name: Build and test (${{ matrix.platform }})
strategy:
fail-fast: true
matrix:
include:
- platform: linux
runner: ubuntu-latest
timeout: 30
- platform: windows
runner: windows-latest
timeout: 45
runs-on: ${{ matrix.runner }}
timeout-minutes: ${{ matrix.timeout }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v7
with:
submodules: true
- id: toolchain
if: matrix.platform == 'windows'
uses: actions/cache@v6
with:
path: C:\winlibs
key: ${{ env.WINLIBS_ZIP }}
- if: matrix.platform == 'windows' && steps.toolchain.outputs.cache-hit != 'true'
run: |
curl -sSfL -o "$RUNNER_TEMP/winlibs.zip" "https://github.com/brechtsanders/winlibs_mingw/releases/download/$WINLIBS_RELEASE/$WINLIBS_ZIP"
7z x "$RUNNER_TEMP/winlibs.zip" -oC:/winlibs
- if: matrix.platform == 'windows'
run: echo 'C:\winlibs\mingw64\bin' >> "$GITHUB_PATH"
- uses: hendrikmuhs/ccache-action@v1.2.24
with:
key: ${{ matrix.platform }}
save: false
- name: Configure Linux
if: matrix.platform == 'linux'
run: cmake -S . -B build $CONFIGURE
- name: Configure Windows
if: matrix.platform == 'windows'
shell: cmd
run: cmake -S . -B build %CONFIGURE%
- name: Build project and tests
run: cmake --build build
- name: Run tests
run: ctest --test-dir build --output-on-failure --timeout 120 --stop-on-failure --no-tests=error
- name: Build patched libraries
run: cmake --build build --target libs
- name: Package release files
run: python tools/package_release.py --platform "${{ matrix.platform }}" --build build --output dist
- uses: actions/upload-artifact@v7
with:
name: release-${{ matrix.platform }}
path: dist/*
if-no-files-found: error
compression-level: 0
publish:
name: Publish release
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RELEASE_TAG: ${{ github.ref_name }}
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
pattern: release-*
merge-multiple: true
path: dist
- name: Check release files and collect documentation
shell: bash
run: |
for file in prx-windows.zip prx-windows.tar.gz prx-linux.zip prx-linux.tar.gz relinker.exe relinker; do
test -s "dist/$file"
done
python tools/package_release.py --docs docs/user --output dist
chmod +x dist/relinker
- name: Create draft and upload files
run: gh release create "$RELEASE_TAG" dist/* --verify-tag --draft --title "$RELEASE_TAG" --generate-notes
- name: Publish complete release
run: gh release edit "$RELEASE_TAG" --draft=false

View File

@ -4,21 +4,21 @@ Tool for automatic executables porting to Linux and Windows.
Includes a [relinker](core/relinker) that converts executable to the target system's native format and implementations of [system prx libraries](core/libs/prx) suitable for dynamic linking. No emulation or separate runtime process.
## Status
[![libraries](https://boykopovar.github.io/AnyPS5/badge-libraries.svg)](https://boykopovar.github.io/AnyPS5/) [![shaders](https://boykopovar.github.io/AnyPS5/badge-shaders.svg)](https://boykopovar.github.io/AnyPS5/)
[![progress map](https://boykopovar.github.io/AnyPS5/progress.svg)](https://boykopovar.github.io/AnyPS5/)
<sub>* System libraries: percentage of the functions known to the project so far (declared in [core/libs/prx](core/libs/prx)), not of every PS5 system function. The total grows as more functions are declared.</sub>
[List of verified games](docs/user/COMPATIBILITY.md)
Releases will be published after the first full successful launch of at least one game.
Dreaming Sarah (2D platformer) runs at a stable 60 fps on a GTX 1050 Ti / i5-7500 3.4GHz.
## Status
Unsupported or unexpected states strictly throw `std::runtime_error`. `what()` is printed to stderr and the process terminates.
Execution reaches `_start`, [stack unwinding](core/libs/prx/libc/src/exception/Unwind.cpp) and exception handling tables are built, reaches main. Unsupported or unexpected states strictly throw `std::runtime_error`. `what()` is printed to stderr and the process terminates.
The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully produces SPIR-V, validated via [Spirv-Tools](3rdparty/SPIRV-Tools) when built with `ANYPS5_ENABLE_SPIRV_TOOLS`.
The real game reaches the logo, main menu, and [gameplay](https://gist.github.com/user-attachments/assets/81d28e9b-c237-4545-b2ca-720071129816) with audio.
The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully produces SPIR-V (validated via [Spirv-Tools](3rdparty/SPIRV-Tools) when built with `ANYPS5_ENABLE_SPIRV_TOOLS`).
[Technical debt of the project](docs/dev/TechnicalDebt.md), [code style conventions](docs/dev/CONVENTIONS.md)

View File

@ -1,5 +1,5 @@
# Compatibility
# Tested compatibility
| Game | ID | Windows | Linux | GTX 1050 Ti / i5-7500 |
|----------------|-----------|-------------------|-------|-----------------------|
| Dreaming Sarah | PPSA02929 | In game, playable | ? | 60 FPS |
| Game | ID | Windows | Linux | GTX 1050 Ti / i5-7500 3.4GHz | Intel(R) HD Graphic 620 / i5-7200 2.5GHz |
|----------------|-----------|-------------------|-------|------------------------------|------------------------------------------|
| Dreaming Sarah | PPSA02929 | In game, playable | ? | 60 FPS | 36 FPS |

66
tools/package_release.py Normal file
View File

@ -0,0 +1,66 @@
import argparse
import shutil
import tarfile
import zipfile
from pathlib import Path
def package(platform, build, output):
libraries = sorted((build / "core/libs/libs").glob("*.prx"))
expected = {f"{directory.name}.prx" for directory in Path("core/libs/prx").iterdir() if directory.is_dir()}
expected.add("libcohtml.Prospero.prx")
missing = expected - {library.name for library in libraries}
if missing:
raise RuntimeError(f"Missing patched libraries: {', '.join(sorted(missing))}")
executable = "relinker.exe" if platform == "windows" else "relinker"
files = list(libraries)
if platform == "windows":
runtime = Path("C:/winlibs/mingw64/bin")
files.extend(runtime / name for name in ("libgcc_s_seh-1.dll", "libstdc++-6.dll", "libwinpthread-1.dll"))
binary = build / "core/relinker" / executable
for file in [*files, binary]:
if not file.is_file() or file.stat().st_size == 0:
raise RuntimeError(f"Missing or empty release file: {file}")
output.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(output / f"prx-{platform}.zip", "w", compression=zipfile.ZIP_DEFLATED) as archive:
for file in files:
archive.write(file, arcname=file.name)
with tarfile.open(output / f"prx-{platform}.tar.gz", "w:gz") as archive:
for file in files:
archive.add(file, arcname=file.name)
shutil.copy2(binary, output / executable)
def collect_docs(source, output):
documents = sorted(source.rglob("*.md"))
if not documents:
raise RuntimeError(f"No Markdown documents found in {source}")
names = set()
for document in documents:
if document.name in names or (output / document.name).exists():
raise RuntimeError(f"Duplicate release asset name: {document.name}")
names.add(document.name)
output.mkdir(parents=True, exist_ok=True)
for document in documents:
shutil.copy2(document, output / document.name)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--platform", choices=("linux", "windows"))
parser.add_argument("--build", type=Path)
parser.add_argument("--docs", type=Path)
parser.add_argument("--output", required=True, type=Path)
args = parser.parse_args()
if args.docs is not None:
if args.platform is not None or args.build is not None:
parser.error("--docs cannot be combined with --platform or --build")
collect_docs(args.docs, args.output)
else:
if args.platform is None or args.build is None:
parser.error("--platform and --build are required when --docs is not specified")
package(args.platform, args.build, args.output)
if __name__ == "__main__":
main()

View File

@ -26,7 +26,7 @@ OPCODE_ALIASES = {
}
REPORT_ROWS = 100
PANEL_WIDTH, GAP, MAP_HEIGHT, HEADER = 495, 10, 280, 30
DONE_COLOR, TODO_COLOR, BORDER, TEXT = "#2ea043", "#1f6feb", "#0d1117", "#ffffff"
DONE_COLOR, TODO_COLOR, BORDER, TEXT = "#2ea043", "#6e7681", "#0d1117", "#ffffff"
def body_end(text, start):