From 00261033fab0dd3d0c00ad97b3ad1eb2ea32f795 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Mon, 28 Sep 2026 22:58:17 +0300 Subject: [PATCH 1/3] refactor(docs): separated user and dev in docs + compatibility file --- README.md | 8 ++++-- core/libs/prx/libcohtml/DEPLOY.md | 2 +- core/libs/prx/libfmod/Export.cpp | 2 +- docs/TechnicalDebt.md | 41 ------------------------------- docs/{ => dev}/CONVENTIONS.md | 0 docs/dev/TechnicalDebt.md | 41 +++++++++++++++++++++++++++++++ docs/user/COMPATIBILITY.md | 5 ++++ docs/{ => user}/INPUT_MAPPING.md | 0 8 files changed, 54 insertions(+), 45 deletions(-) delete mode 100644 docs/TechnicalDebt.md rename docs/{ => dev}/CONVENTIONS.md (100%) create mode 100644 docs/dev/TechnicalDebt.md create mode 100644 docs/user/COMPATIBILITY.md rename docs/{ => user}/INPUT_MAPPING.md (100%) diff --git a/README.md b/README.md index 8a780366bb..47e39fe688 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully prod The real game reaches the logo, main menu, and [gameplay](https://gist.github.com/user-attachments/assets/81d28e9b-c237-4545-b2ca-720071129816) with audio. -[Technical debt of the project](docs/TechnicalDebt.md), [code style conventions](docs/CONVENTIONS.md) +[Technical debt of the project](docs/dev/TechnicalDebt.md), [code style conventions](docs/dev/CONVENTIONS.md) ## Build @@ -30,9 +30,13 @@ The relinker uses only the C++20 standard library and should build with any conf The project targets maximum compiler portability. Support for additional compilers will be addressed after the first successful game launch. +## Compatibility + +See the [game compatibility list](docs/user/COMPATIBILITY.md) for tested games and known issues. + ## Input mapping -SDL-mapped game controllers are supported, including analog sticks and triggers. Keyboard and mouse controls can be configured with an `anyps5-input.ini` file. See [input mapping](docs/INPUT_MAPPING.md) for the supported devices and configuration format. +SDL-mapped game controllers are supported, including analog sticks and triggers. Keyboard and mouse controls can be configured with an `anyps5-input.ini` file. See [input mapping](docs/user/INPUT_MAPPING.md) for the supported devices and configuration format. ## Disclaimer diff --git a/core/libs/prx/libcohtml/DEPLOY.md b/core/libs/prx/libcohtml/DEPLOY.md index de4c3ffea8..0b4cbdc5da 100644 --- a/core/libs/prx/libcohtml/DEPLOY.md +++ b/core/libs/prx/libcohtml/DEPLOY.md @@ -5,7 +5,7 @@ Game `DT_NEEDED` expects exactly: `libcohtml.Prospero.prx`. Copy next to the relinked eboot (same directory): - `build/core/libs/libs/libcohtml.Prospero.prx` (patched build output, 50 NIDs) — required. -- `libgcc_s_seh-1.dll`, `libstdc++-6.dll`, `libwinpthread-1.dll` from `winlibs-gcc15/mingw64/bin` — required on Windows (see `docs/TechnicalDebt.md`). +- `libgcc_s_seh-1.dll`, `libstdc++-6.dll`, `libwinpthread-1.dll` from `winlibs-gcc15/mingw64/bin` — required on Windows (see [technical debt](../../../../docs/dev/TechnicalDebt.md)). Do NOT deploy: diff --git a/core/libs/prx/libfmod/Export.cpp b/core/libs/prx/libfmod/Export.cpp index 6c5a7476ff..bd7545a649 100644 --- a/core/libs/prx/libfmod/Export.cpp +++ b/core/libs/prx/libfmod/Export.cpp @@ -1635,7 +1635,7 @@ public: } else { // Compressed (Vorbis/MP3/FSB/AT9...) or unknown: audible // placeholder so the mixer path can be heard. Real decoders - // (Vorbis/MP3/AT9) remain future work — see docs/TechnicalDebt. + // (Vorbis/MP3/AT9) remain future work — see docs/dev/TechnicalDebt. FmodMakeSine(created->pcm, kFmodOutRate, 2, 440.0f, 1.0f); created->pcmRate = kFmodOutRate; created->pcmCh = 2; diff --git a/docs/TechnicalDebt.md b/docs/TechnicalDebt.md deleted file mode 100644 index 3b2246721e..0000000000 --- a/docs/TechnicalDebt.md +++ /dev/null @@ -1,41 +0,0 @@ -# Project technical debt - -### Build - -- Building on Windows requires a specific version of mingw - MinGW-w64 GCC 15.2.0 (`winlibs-gcc15`, `x86_64-ucrt-posix-seh`) -- Even compiled prx libraries on Windows require nearby (static linking of these dependencies causes conflicts): - - libgcc_s_seh-1.dll - - libstdc++-6.dll - - libwinpthread-1.dll - -### Silent stubs - -Throughout the project, every function at every stage either **does exactly what it's supposed to or throws an exception**. Everywhere... except: -- [libSceSaveDataDialog.native](../core/libs/prx/libSceSaveDataDialog.native/Export.cpp) -- [libSceCommonDialog](../core/libs/prx/libSceCommonDialog/Export.cpp) -- The shader recompiler [skips baryctric coordinates](../core/shader/recompiler/Recompiler.cpp) (is not even passed to SpirvTargetOptions at row 212). - -### Unknown function info - -- [zARR5aCmkoY](../core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp) (libSceAgc) - unknown name, signature -- [qj7QZpgr9Uw](../core/libs/prx/libSceAgc/DcbState/src/ContextState.cpp) (libSceAgc) - unknown name -- [fd5Bp5tGTgo](../core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp) (libSceAgc) - unknown name -- [dolOmWH+huQ](../core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp) (libSceAgc) - unknown name -- [V++UgBtQhn0](../core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp) (libSceAgc) - unknown name -- [gQkqkLttcpw](../core/libs/prx/libSceAgc/Acb/src/Control.cpp) (libSceAgc) - unknown name, signature -- [sceKernelInternalMemoryGetModuleSegmentInfo](../core/libs/prx/libkernel/Module/src/Module.cpp) (libkernel) - unknown signature -- [sceLibcInternalBacktraceForGame](../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - unknown signature -- [sceLibcInternalHeapErrorReportForGame](../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - unknown signature -- [__progname](../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - unknown data export - -### Functional - -- [Shader recompilation](../core/shader/recompiler/Recompiler.cpp) currently occurs right before it was transferred to Vulkan with caching, but should be moved to the [relinker](../core/relinker/main.cpp) stage. For this purpose, [shader/recompiler](../core/shader/recompiler) was written completely independently from [libs/prx](../core/libs/prx). -- The executable file that [relinker](../core/relinker/elfpatcher/src/windows/WindowsPeWriter.cpp) generates opens the console when launched, which is inconvenient for playability. -- [Relinker](../core/relinker/elfpatcher/src) doesn't add an icon to the generated executable. This should be done without adding dependencies (only standard). -- `--to-intel` does not lower the register form of INSERTQ (`F2 0F 79`) nor MONITORX/MWAITX/CLZERO/RDPRU/MCOMMIT; the [lowering](../core/relinker/codegen/src/x86/Sse4aLowering.cpp) fails the relink instead. SHA-NI is not substituted. -- The length-changing path of the [instruction rewriter](../core/relinker/codegen/src/x86/X64InstructionRewriter.cpp) is not used by the [converter](../core/relinker/codegen/src/Amd64OnlyConverter.cpp): it does not adjust VEX/0F38/0F3A RIP-relative operands, data-to-code references (relocations, FDEs, jump tables) or segment sizes, so every substitution keeps the instruction length. -- The Linux placement of the `--to-intel` stubs in the [ELF patcher](../core/relinker/elfpatcher/src/linux/LinuxElfPatcher.cpp) is covered only by a synthetic test. -- The libc [SSE4a trap emulation](../core/libs/prx/libc/src/specifics/windows/Sse4aEmulation.hpp) on Windows is superseded by `--to-intel` and remains only until the relinked title has been verified without it. -- `--to-intel` lowerings that need a stub section are rejected for `sce_module` guest modules: the [guest module builder](../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) does not pass stub sites to the [guest module writer](../core/relinker/elfpatcher/include/elfpatcher/general/GuestModuleWriter.hpp). -- The game can expect its modified prx from the `sce_module`/`sce_modules` folder - this support is not implemented. diff --git a/docs/CONVENTIONS.md b/docs/dev/CONVENTIONS.md similarity index 100% rename from docs/CONVENTIONS.md rename to docs/dev/CONVENTIONS.md diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md new file mode 100644 index 0000000000..8468605e01 --- /dev/null +++ b/docs/dev/TechnicalDebt.md @@ -0,0 +1,41 @@ +# Project technical debt + +### Build + +- Building on Windows requires a specific version of mingw - MinGW-w64 GCC 15.2.0 (`winlibs-gcc15`, `x86_64-ucrt-posix-seh`) +- Even compiled prx libraries on Windows require nearby (static linking of these dependencies causes conflicts): + - libgcc_s_seh-1.dll + - libstdc++-6.dll + - libwinpthread-1.dll + +### Silent stubs + +Throughout the project, every function at every stage either **does exactly what it's supposed to or throws an exception**. Everywhere... except: +- [libSceSaveDataDialog.native](../../core/libs/prx/libSceSaveDataDialog.native/Export.cpp) +- [libSceCommonDialog](../../core/libs/prx/libSceCommonDialog/Export.cpp) +- The shader recompiler [skips baryctric coordinates](../../core/shader/recompiler/Recompiler.cpp) (is not even passed to SpirvTargetOptions at row 212). + +### Unknown function info + +- [zARR5aCmkoY](../../core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp) (libSceAgc) - unknown name, signature +- [qj7QZpgr9Uw](../../core/libs/prx/libSceAgc/DcbState/src/ContextState.cpp) (libSceAgc) - unknown name +- [fd5Bp5tGTgo](../../core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp) (libSceAgc) - unknown name +- [dolOmWH+huQ](../../core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp) (libSceAgc) - unknown name +- [V++UgBtQhn0](../../core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp) (libSceAgc) - unknown name +- [gQkqkLttcpw](../../core/libs/prx/libSceAgc/Acb/src/Control.cpp) (libSceAgc) - unknown name, signature +- [sceKernelInternalMemoryGetModuleSegmentInfo](../../core/libs/prx/libkernel/Module/src/Module.cpp) (libkernel) - unknown signature +- [sceLibcInternalBacktraceForGame](../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - unknown signature +- [sceLibcInternalHeapErrorReportForGame](../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - unknown signature +- [__progname](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - unknown data export + +### Functional + +- [Shader recompilation](../../core/shader/recompiler/Recompiler.cpp) currently occurs right before it was transferred to Vulkan with caching, but should be moved to the [relinker](../../core/relinker/main.cpp) stage. For this purpose, [shader/recompiler](../../core/shader/recompiler) was written completely independently from [libs/prx](../../core/libs/prx). +- The executable file that [relinker](../../core/relinker/elfpatcher/src/windows/WindowsPeWriter.cpp) generates opens the console when launched, which is inconvenient for playability. +- [Relinker](../../core/relinker/elfpatcher/src) doesn't add an icon to the generated executable. This should be done without adding dependencies (only standard). +- `--to-intel` does not lower the register form of INSERTQ (`F2 0F 79`) nor MONITORX/MWAITX/CLZERO/RDPRU/MCOMMIT; the [lowering](../../core/relinker/codegen/src/x86/Sse4aLowering.cpp) fails the relink instead. SHA-NI is not substituted. +- The length-changing path of the [instruction rewriter](../../core/relinker/codegen/src/x86/X64InstructionRewriter.cpp) is not used by the [converter](../../core/relinker/codegen/src/Amd64OnlyConverter.cpp): it does not adjust VEX/0F38/0F3A RIP-relative operands, data-to-code references (relocations, FDEs, jump tables) or segment sizes, so every substitution keeps the instruction length. +- The Linux placement of the `--to-intel` stubs in the [ELF patcher](../../core/relinker/elfpatcher/src/linux/LinuxElfPatcher.cpp) is covered only by a synthetic test. +- The libc [SSE4a trap emulation](../../core/libs/prx/libc/src/specifics/windows/Sse4aEmulation.hpp) on Windows is superseded by `--to-intel` and remains only until the relinked title has been verified without it. +- `--to-intel` lowerings that need a stub section are rejected for `sce_module` guest modules: the [guest module builder](../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) does not pass stub sites to the [guest module writer](../../core/relinker/elfpatcher/include/elfpatcher/general/GuestModuleWriter.hpp). +- The game can expect its modified prx from the `sce_module`/`sce_modules` folder - this support is not implemented. diff --git a/docs/user/COMPATIBILITY.md b/docs/user/COMPATIBILITY.md new file mode 100644 index 0000000000..45a3700215 --- /dev/null +++ b/docs/user/COMPATIBILITY.md @@ -0,0 +1,5 @@ +# Compatibility + +| Game | ID | Windows | Linux | GTX 1050 Ti / i5-7500 | +|----------------|-----------|-------------------|-------|-----------------------| +| Dreaming Sarah | PPSA02929 | In game, playable | ? | 60 FPS | diff --git a/docs/INPUT_MAPPING.md b/docs/user/INPUT_MAPPING.md similarity index 100% rename from docs/INPUT_MAPPING.md rename to docs/user/INPUT_MAPPING.md From 1071c9e09c1400d11a32f9b57b9c95ef240797db Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 29 Sep 2026 00:15:21 +0300 Subject: [PATCH 2/3] feat(ci): add release workflow + update readme --- .github/workflows/release.yml | 111 ++++++++++++++++++++++++++++++++++ README.md | 16 ++--- docs/user/COMPATIBILITY.md | 8 +-- tools/package_release.py | 66 ++++++++++++++++++++ tools/progress.py | 2 +- 5 files changed, 190 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 tools/package_release.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..04afa2cd82 --- /dev/null +++ b/.github/workflows/release.yml @@ -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 diff --git a/README.md b/README.md index 47e39fe688..4b658c4715 100644 --- a/README.md +++ b/README.md @@ -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/) -* 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. +[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) diff --git a/docs/user/COMPATIBILITY.md b/docs/user/COMPATIBILITY.md index 45a3700215..4b62cc2934 100644 --- a/docs/user/COMPATIBILITY.md +++ b/docs/user/COMPATIBILITY.md @@ -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 | diff --git a/tools/package_release.py b/tools/package_release.py new file mode 100644 index 0000000000..c8b7d0c68f --- /dev/null +++ b/tools/package_release.py @@ -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() diff --git a/tools/progress.py b/tools/progress.py index 2af9611d17..2af716e400 100644 --- a/tools/progress.py +++ b/tools/progress.py @@ -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): From 1bc7f0151148c85a6d673cee4837ad2f36562f5c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 29 Sep 2026 00:22:44 +0300 Subject: [PATCH 3/3] feat(ci): add version to release file names --- .github/workflows/release.yml | 8 ++++---- tools/package_release.py | 23 ++++++++++++++--------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 04afa2cd82..aa0ec6803f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,7 @@ concurrency: cancel-in-progress: false env: + RELEASE_TAG: ${{ github.ref_name }} CONFIGURE: >- -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ @@ -71,7 +72,7 @@ jobs: - 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 + run: python tools/package_release.py --platform "${{ matrix.platform }}" --build build --output dist --version "$RELEASE_TAG" - uses: actions/upload-artifact@v7 with: name: release-${{ matrix.platform }} @@ -89,7 +90,6 @@ jobs: env: GH_TOKEN: ${{ github.token }} GH_REPO: ${{ github.repository }} - RELEASE_TAG: ${{ github.ref_name }} steps: - uses: actions/checkout@v7 - uses: actions/download-artifact@v8 @@ -100,11 +100,11 @@ jobs: - 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 + for file in "prx-windows-$RELEASE_TAG.zip" "prx-windows-$RELEASE_TAG.tar.gz" "prx-linux-$RELEASE_TAG.zip" "prx-linux-$RELEASE_TAG.tar.gz" "relinker-$RELEASE_TAG.exe" "relinker-$RELEASE_TAG"; do test -s "dist/$file" done python tools/package_release.py --docs docs/user --output dist - chmod +x dist/relinker + chmod +x "dist/relinker-$RELEASE_TAG" - 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 diff --git a/tools/package_release.py b/tools/package_release.py index c8b7d0c68f..86209fb60c 100644 --- a/tools/package_release.py +++ b/tools/package_release.py @@ -1,11 +1,14 @@ import argparse +import re import shutil import tarfile import zipfile from pathlib import Path -def package(platform, build, output): +def package(platform, build, output, version): + if not re.fullmatch(r"v[0-9A-Za-z][0-9A-Za-z._-]*", version) or version.endswith("."): + raise ValueError(f"Invalid release tag for asset filenames: {version}") 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") @@ -22,13 +25,14 @@ def package(platform, build, output): 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: + with zipfile.ZipFile(output / f"prx-{platform}-{version}.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: + with tarfile.open(output / f"prx-{platform}-{version}.tar.gz", "w:gz") as archive: for file in files: archive.add(file, arcname=file.name) - shutil.copy2(binary, output / executable) + asset = f"relinker-{version}.exe" if platform == "windows" else f"relinker-{version}" + shutil.copy2(binary, output / asset) def collect_docs(source, output): @@ -49,17 +53,18 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument("--platform", choices=("linux", "windows")) parser.add_argument("--build", type=Path) + parser.add_argument("--version") 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") + if args.platform is not None or args.build is not None or args.version is not None: + parser.error("--docs cannot be combined with --platform, --build or --version") 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 args.platform is None or args.build is None or args.version is None: + parser.error("--platform, --build and --version are required when --docs is not specified") + package(args.platform, args.build, args.output, args.version) if __name__ == "__main__":