Initial SyncGames tree: agent, Android, deploy, docs.
Session-gated MinIO save sync with AppImage GUI, CLI edit/session flow, and Gitea release helper. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=SyncGames
|
||||
Comment=Session-gated game save sync (setup and management)
|
||||
Exec=SyncGames
|
||||
Icon=syncgames
|
||||
Categories=Utility;
|
||||
Terminal=false
|
||||
Executable
+117
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build SyncGames-x86_64.AppImage (thin GUI + agent).
|
||||
# Usage: from SyncGames/agent/, ./packaging/build-appimage.sh
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
VERSION="$(python3 -c "from syncgames import __version__; print(__version__)" 2>/dev/null || echo "0.1.0")"
|
||||
# Prefer package version when installed in venv
|
||||
if [[ -d "${ROOT}/.venv" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "${ROOT}/.venv/bin/activate"
|
||||
VERSION="$(python3 -c "from syncgames import __version__; print(__version__)")"
|
||||
fi
|
||||
|
||||
OUT_NAME="SyncGames-${VERSION}-x86_64.AppImage"
|
||||
APPDIR="${ROOT}/build/appimage/AppDir"
|
||||
BUILD_BIN="${ROOT}/build/appimage"
|
||||
APPIMAGETOOL_URL="${APPIMAGETOOL_URL:-https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage}"
|
||||
APPIMAGETOOL="${APPIMAGETOOL:-${BUILD_BIN}/appimagetool-x86_64.AppImage}"
|
||||
|
||||
echo "==> Version: ${VERSION}"
|
||||
echo "==> Output: ${OUT_NAME}"
|
||||
|
||||
if [[ ! -d "${ROOT}/.venv" ]]; then
|
||||
echo "==> Creating .venv"
|
||||
python3 -m venv "${ROOT}/.venv"
|
||||
fi
|
||||
# shellcheck source=/dev/null
|
||||
source "${ROOT}/.venv/bin/activate"
|
||||
python3 -m pip install -q -U pip
|
||||
python3 -m pip install -q -e ".[build]"
|
||||
|
||||
echo "==> PyInstaller"
|
||||
rm -rf "${ROOT}/build/pyinstaller" "${ROOT}/dist/SyncGames" || true
|
||||
pyinstaller "${ROOT}/packaging/pyinstaller.spec"
|
||||
|
||||
if [[ ! -x "${ROOT}/dist/SyncGames/SyncGames" ]]; then
|
||||
echo "PyInstaller did not produce dist/SyncGames/SyncGames" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Assemble AppDir"
|
||||
rm -rf "$APPDIR"
|
||||
mkdir -p "$APPDIR/usr/bin" \
|
||||
"$APPDIR/usr/lib" \
|
||||
"$APPDIR/usr/share/applications" \
|
||||
"$APPDIR/usr/share/icons/hicolor/256x256/apps" \
|
||||
"$APPDIR/usr/share/icons/hicolor/128x128/apps" \
|
||||
"$APPDIR/usr/share/metainfo"
|
||||
|
||||
cp -a "${ROOT}/dist/SyncGames" "$APPDIR/usr/lib/syncgames"
|
||||
ln -sf "../lib/syncgames/SyncGames" "$APPDIR/usr/bin/SyncGames"
|
||||
|
||||
python3 - <<'PY'
|
||||
from PIL import Image
|
||||
from pathlib import Path
|
||||
|
||||
base = Path("build/appimage/AppDir/usr/share/icons/hicolor")
|
||||
color = (24, 72, 64, 255) # deep teal — save/sync feel, not purple AI default
|
||||
for size in (256, 128):
|
||||
p = base / f"{size}x{size}" / "apps" / "syncgames.png"
|
||||
p.parent.mkdir(parents=True, exist_ok=True)
|
||||
Image.new("RGBA", (size, size), color).save(p)
|
||||
PY
|
||||
|
||||
cp "$ROOT/packaging/appimage/syncgames.desktop" "$APPDIR/usr/share/applications/"
|
||||
cp "$APPDIR/usr/share/applications/syncgames.desktop" "$APPDIR/"
|
||||
|
||||
cat > "$APPDIR/usr/share/metainfo/io.github.syncgames.appdata.xml" <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>io.github.syncgames</id>
|
||||
<name>SyncGames</name>
|
||||
<summary>Session-gated game save sync setup and management</summary>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>MIT</project_license>
|
||||
<description>
|
||||
<p>Configure MinIO SSOT connection, manage game save paths, and run start/end session sync with lease and version guardrails.</p>
|
||||
</description>
|
||||
<launchable type="desktop-id">syncgames.desktop</launchable>
|
||||
<content_rating type="oars-1.1"/>
|
||||
<releases>
|
||||
<release version="${VERSION}"/>
|
||||
</releases>
|
||||
</component>
|
||||
EOF
|
||||
|
||||
cat > "$APPDIR/AppRun" <<'SH'
|
||||
#!/bin/bash
|
||||
HERE="$(dirname "$(readlink -f "$0")")"
|
||||
export PATH="${HERE}/usr/bin:${PATH}"
|
||||
LIBDIR="${HERE}/usr/lib/syncgames"
|
||||
export LD_LIBRARY_PATH="${LIBDIR}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
if [[ -d "${LIBDIR}/PySide6" ]]; then
|
||||
export QT_PLUGIN_PATH="${LIBDIR}/PySide6/Qt/plugins${QT_PLUGIN_PATH:+:$QT_PLUGIN_PATH}"
|
||||
fi
|
||||
exec "${HERE}/usr/bin/SyncGames" "$@"
|
||||
SH
|
||||
chmod +x "$APPDIR/AppRun"
|
||||
|
||||
ln -sf "usr/share/icons/hicolor/256x256/apps/syncgames.png" "$APPDIR/.DirIcon" || true
|
||||
# appimagetool expects Icon= name at AppDir root
|
||||
cp "$APPDIR/usr/share/icons/hicolor/256x256/apps/syncgames.png" "$APPDIR/syncgames.png"
|
||||
|
||||
echo "==> appimagetool"
|
||||
mkdir -p "$BUILD_BIN"
|
||||
if [[ ! -x "$APPIMAGETOOL" ]]; then
|
||||
curl -fsSL -o "$APPIMAGETOOL" "$APPIMAGETOOL_URL"
|
||||
chmod +x "$APPIMAGETOOL"
|
||||
fi
|
||||
|
||||
rm -f "${ROOT}/dist/${OUT_NAME}" || true
|
||||
mkdir -p "${ROOT}/dist"
|
||||
ARCH=x86_64 "$APPIMAGETOOL" "$APPDIR" "${ROOT}/dist/${OUT_NAME}"
|
||||
echo "==> Built ${ROOT}/dist/${OUT_NAME}"
|
||||
@@ -0,0 +1,6 @@
|
||||
"""PyInstaller / AppImage entry — launches SyncGames thin GUI."""
|
||||
|
||||
from syncgames.gui.app import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,68 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
# From SyncGames/agent: pyinstaller packaging/pyinstaller.spec
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from PyInstaller.utils.hooks import collect_submodules
|
||||
|
||||
project_root = Path(os.path.dirname(os.path.abspath(SPEC))).parent
|
||||
repo_root = project_root.parent
|
||||
block_cipher = None
|
||||
|
||||
hidden = collect_submodules("syncgames") + ["boto3", "botocore", "psutil"]
|
||||
|
||||
datas = []
|
||||
games_src = repo_root / "config" / "games"
|
||||
if games_src.is_dir():
|
||||
for toml in sorted(games_src.glob("*.toml")):
|
||||
datas.append((str(toml), "bundled_config/games"))
|
||||
systemd_src = repo_root / "systemd"
|
||||
if systemd_src.is_dir():
|
||||
for unit in ("syncgames-agent.service", "syncgames-watch@.service", "install-user-units.sh"):
|
||||
p = systemd_src / unit
|
||||
if p.exists():
|
||||
datas.append((str(p), "bundled_systemd"))
|
||||
|
||||
a = Analysis(
|
||||
[str(project_root / "packaging" / "entrypoint.py")],
|
||||
pathex=[str(project_root)],
|
||||
binaries=[],
|
||||
datas=datas,
|
||||
hiddenimports=hidden,
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name="SyncGames",
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name="SyncGames",
|
||||
)
|
||||
Reference in New Issue
Block a user