mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-27 10:38:46 -04:00
874df9b5bb
Retire the setup-host release model (CI wiped generated/ and shipped
emitters + sources + a wizard so every player regenerated and rebuilt the
title). Titles now commit generated/ and CI ships the compiled game.
- docs/ci/templates/game-release.yml replaces setup-release.yml: gate on
committed game C (tools/ci/check_generated.sh), emit the OpenBIOS backend
in CI (tools/ci/generate_openbios.sh), configure with
PSXRECOMP_REQUIRE_GAME_C=ON and assert the full path was taken, package
with tools/package_game_release.sh, verify the zip is a game not a kit.
- tools/package_game_release.sh: exe, assets, bundled OpenBIOS, configs, mod
catalog + overlay toolchain via release_stage.py, notices; refuses sources,
emitters, generated C, BIOS dumps. clear_generated.sh removed;
package_setup_host.sh marked retired.
- Scaffold: gitignore templates track generated/, package_release.sh.in
wrapper, setup_project.{sh,ps1} commit generated/ after Generate.
- Project Studio: audit flags ignored/untracked generated/, retail BIOS C
and setup-host workflows; merge_gitignore un-ignores generated/; ops emit
the bundled packager + workflow; generate_ci uses the new template.
- Docs: docs/ci/BUNDLED_RELEASES.md replaces HOST_ONLY_RELEASES.md.
- Tests: tools/tests/test_bundled_release_layout.py; fix pre-existing
false positives in the packager-surface and overlay-static tests.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
144 lines
3.6 KiB
Python
144 lines
3.6 KiB
Python
"""Data models for audit / plan / apply."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field, asdict
|
|
from enum import Enum
|
|
from typing import Any
|
|
|
|
|
|
class Severity(str, Enum):
|
|
REQUIRED = "required"
|
|
RECOMMENDED = "recommended"
|
|
OPTIONAL = "optional"
|
|
INFO = "info"
|
|
|
|
|
|
class CheckStatus(str, Enum):
|
|
PASS = "pass"
|
|
FAIL = "fail"
|
|
WARN = "warn"
|
|
SKIP = "skip"
|
|
|
|
|
|
class LayoutClass(str, Enum):
|
|
SCAFFOLD_COMPLETE = "scaffold-complete"
|
|
SETUP_HOST_PARTIAL = "setup-host-partial"
|
|
LEGACY_PACKAGING = "legacy-packaging"
|
|
UNKNOWN = "unknown"
|
|
|
|
|
|
@dataclass
|
|
class CheckResult:
|
|
id: str
|
|
title: str
|
|
status: CheckStatus
|
|
severity: Severity
|
|
detail: str = ""
|
|
fix_op: str | None = None
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
d = asdict(self)
|
|
d["status"] = self.status.value
|
|
d["severity"] = self.severity.value
|
|
return d
|
|
|
|
|
|
@dataclass
|
|
class AuditReport:
|
|
root: str
|
|
layout: LayoutClass
|
|
project_name: str
|
|
boot_exe: str | None
|
|
checks: list[CheckResult] = field(default_factory=list)
|
|
notes: list[str] = field(default_factory=list)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"root": self.root,
|
|
"layout": self.layout.value,
|
|
"project_name": self.project_name,
|
|
"boot_exe": self.boot_exe,
|
|
"checks": [c.to_dict() for c in self.checks],
|
|
"notes": list(self.notes),
|
|
"fail_count": sum(1 for c in self.checks if c.status == CheckStatus.FAIL),
|
|
"warn_count": sum(1 for c in self.checks if c.status == CheckStatus.WARN),
|
|
}
|
|
|
|
def failing_ops(self) -> list[str]:
|
|
ops: list[str] = []
|
|
for c in self.checks:
|
|
if c.status in (CheckStatus.FAIL, CheckStatus.WARN) and c.fix_op:
|
|
if c.fix_op not in ops:
|
|
ops.append(c.fix_op)
|
|
return ops
|
|
|
|
|
|
@dataclass
|
|
class MigrateOptions:
|
|
"""User choices for apply (bundled releases: committed generated/ C)."""
|
|
|
|
disc: str | None = None
|
|
project_name: str | None = None
|
|
boot_exe: str | None = None
|
|
players: int = 2
|
|
zip_prefix: str | None = None
|
|
github_owner: str | None = None
|
|
github_repo: str | None = None
|
|
window_title: str | None = None
|
|
enable_recomp_ui: bool = True
|
|
enable_wizard: bool = True
|
|
enable_netplay: bool = False
|
|
lobby_url: str = "ws://netplay.retcomm.net:8765"
|
|
enable_ci: bool = True
|
|
relocate_boxart: bool = True
|
|
rewrite_cmake: bool = True
|
|
merge_gitignore: bool = True
|
|
probe_disc: bool = False
|
|
record_pins: bool = True
|
|
force: bool = False
|
|
only: list[str] = field(default_factory=list)
|
|
skip: list[str] = field(default_factory=list)
|
|
dry_run: bool = False
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
@dataclass
|
|
class PlanStep:
|
|
op_id: str
|
|
title: str
|
|
detail: str = ""
|
|
selected: bool = True
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
@dataclass
|
|
class Plan:
|
|
root: str
|
|
layout: LayoutClass
|
|
steps: list[PlanStep] = field(default_factory=list)
|
|
options: MigrateOptions = field(default_factory=MigrateOptions)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"root": self.root,
|
|
"layout": self.layout.value,
|
|
"steps": [s.to_dict() for s in self.steps],
|
|
"options": self.options.to_dict(),
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class ApplyResult:
|
|
op_id: str
|
|
ok: bool
|
|
message: str
|
|
changed_paths: list[str] = field(default_factory=list)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|