Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package steam for openSUSE:Factory:NonFree 
checked in at 2024-10-02 21:31:13
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory:NonFree/steam (Old)
 and      /work/SRC/openSUSE:Factory:NonFree/.steam.new.19354 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "steam"

Wed Oct  2 21:31:13 2024 rev:59 rq:1204796 version:1.0.0.81

Changes:
--------
--- /work/SRC/openSUSE:Factory:NonFree/steam/steam.changes      2024-06-13 
15:36:55.419575274 +0200
+++ /work/SRC/openSUSE:Factory:NonFree/.steam.new.19354/steam.changes   
2024-10-02 21:31:15.133062868 +0200
@@ -1,0 +2,8 @@
+Mon Sep 30 15:13:31 UTC 2024 - Callum Farmer <gm...@opensuse.org>
+
+- Update to version 1.0.0.81:
+  - Client timestamp 1721173382 (2024-07-16)
+  - Steam Runtime (scout) version 0.20240610.91380
+- Update RPM constructs (autosetup/patch)
+
+-------------------------------------------------------------------

Old:
----
  steam-streaming.xml
  steam_1.0.0.79.tar.gz

New:
----
  steam_1.0.0.81.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ steam.spec ++++++
--- /var/tmp/diff_new_pack.QpDD9e/_old  2024-10-02 21:31:17.441158820 +0200
+++ /var/tmp/diff_new_pack.QpDD9e/_new  2024-10-02 21:31:17.453159318 +0200
@@ -36,15 +36,14 @@
 %endif \
 %{nil}
 Name:           steam
-Version:        1.0.0.79
+Version:        1.0.0.81
 Release:        0
 Summary:        Installer for Valve's digital software distribution service
 # "Limited Installation License"
 License:        SUSE-Freeware
 Group:          Amusements/Games/Other
 URL:            https://www.steampowered.com/
-Source0:        
https://repo.steampowered.com/steam/pool/steam/s/steam/steam_%{version}.tar.gz
-Source2:        %{name}-streaming.xml
+Source0:        
https://repo.steampowered.com/steam/archive/stable/%{name}_%{version}.tar.gz
 # PATCH-FIX-OPENSUSE steam-path-fix.patch bnc#1025841
 Patch0:         steam-path-fix.patch
 Patch1:         gpu-offload.patch
@@ -125,16 +124,14 @@
 %dependency  libICE6
 %dependency  libSM6
 %dependency  libusb-1_0-0
+%dependency  libpipewire-0_3-0
 %booldependency  libgbm1
 %if 0%{?suse_version} >= 1600 || 0%{?sle_version} >= 150600
 %dependency  libavif16
 %endif
-%if 0%{?suse_version} >= 1550
+%if 0%{?suse_version} >= 1600 || 0%{?sle_version} >= 150500
 %booldependency  libnm0
 %endif
-%if 0%{?suse_version} >= 1550 || 0%{?sle_version} >= 150300
-%dependency  libpipewire-0_3-0
-%endif
 
 %description
 Steam is a software distribution service with an online store, automated
@@ -145,12 +142,12 @@
 complete the installation of the client for the current user.
 
 %prep
-%setup -q -n steam-launcher
+%autosetup -N -n steam-launcher
 %if 0%{?suse_version} < 1550
-%patch0 -p1
+%patch -P 0 -p1
 %endif
 %if 0%{?suse_version} < 1550 && 0%{?sle_version} < 150400
-%patch1 -p1
+%patch -P 1 -p1
 %endif
 
 
@@ -160,20 +157,13 @@
 %install
 make DESTDIR=%{buildroot} install-bin install-docs install-icons 
install-bootstrap install-desktop install-appdata
 
-%if 0%{?suse_version} < 1550 && 0%{?sle_version} < 150300
-install -Dm0644 %{SOURCE2} 
%{buildroot}%{_prefix}/lib/firewalld/services/steam-streaming.xml
-%endif
+
 find %{buildroot} -type f -exec sed -i 's|#!%{_bindir}/env |#!%{_bindir}/|g' 
{} \;
 # Remove steamdeps as it is apt specific
 rm -f %{buildroot}%{_bindir}/%{name}deps
 
 %fdupes -s %{buildroot}
 
-%if 0%{?suse_version} < 1550 && 0%{?sle_version} < 150300
-%post
-%firewalld_reload
-%endif
-
 %files
 %{_bindir}/*
 %{_prefix}/lib/%{name}
@@ -187,9 +177,4 @@
 %doc debian/changelog
 %{_mandir}/man6/%{name}.6%{?ext_man}
 %{_datadir}/metainfo/*
-%if 0%{?suse_version} < 1550 && 0%{?sle_version} < 150300
-%dir %{_prefix}/lib/firewalld
-%dir %{_prefix}/lib/firewalld/services
-%{_prefix}/lib/firewalld/services/steam-streaming.xml
-%endif
 

++++++ steam_1.0.0.79.tar.gz -> steam_1.0.0.81.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/steam-launcher/bin_steam.sh 
new/steam-launcher/bin_steam.sh
--- old/steam-launcher/bin_steam.sh     2024-02-08 16:05:57.000000000 +0100
+++ new/steam-launcher/bin_steam.sh     2024-08-15 19:38:56.000000000 +0200
@@ -21,7 +21,7 @@
     echo "bin_steam.sh[$$]: $*" >&2 || :
 }
 
-export STEAMSCRIPT_VERSION=1.0.0.79
+export STEAMSCRIPT_VERSION=1.0.0.81
 
 # Set up domain for script localization
 export TEXTDOMAIN=steam
@@ -169,6 +169,22 @@
        fi
 }
 
+function forward_command_line()
+{
+       if ! [ -p "$STEAMCONFIG/steam.pipe" ]; then
+               return 1
+       fi
+
+       local runtime="$STEAMCONFIG/root/ubuntu12_32/steam-runtime"
+       local remote="$runtime/amd64/usr/bin/steam-runtime-steam-remote"
+
+       if [ -x "$remote" ] && "$remote" "$@" 2>/dev/null; then
+               return 0
+       else
+               return 1
+       fi
+}
+
 # Don't allow running as root
 if [ "$(id -u)" == "0" ]; then
        show_message --error $"Cannot run as root user"
@@ -178,6 +194,12 @@
 # Look for the Steam data files
 setup_variables
 
+# If Steam is already running, try to forward the command-line to it.
+# If successful, there's nothing more to do.
+if forward_command_line "$@"; then
+       exit 0
+fi
+
 if ! check_bootstrap "$LAUNCHSTEAMDIR"; then
        # See if we just need to recreate the data link
        if check_bootstrap "$DEFAULTSTEAMDIR"; then
@@ -198,8 +220,13 @@
        exit 1
 fi
 
+# Leave a copy of the bootstrap tarball in ~/.steam so that Steam can
+# re-bootstrap itself if required
+if ! cmp -s "$LAUNCHSTEAMBOOTSTRAPFILE" "$LAUNCHSTEAMDIR/bootstrap.tar.xz"; 
then
+    cp "$LAUNCHSTEAMBOOTSTRAPFILE" "$LAUNCHSTEAMDIR/bootstrap.tar.xz"
+fi
+
 # go to the install directory and run the client
-cp "$LAUNCHSTEAMBOOTSTRAPFILE" "$LAUNCHSTEAMDIR/bootstrap.tar.xz"
 cd "$LAUNCHSTEAMDIR"
 
 exec "$LAUNCHSTEAMDIR/$STEAMBOOTSTRAP" "$@"
Binary files old/steam-launcher/bootstraplinux_ubuntu12_32.tar.xz and 
new/steam-launcher/bootstraplinux_ubuntu12_32.tar.xz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/steam-launcher/buildutils/add-client-files.py 
new/steam-launcher/buildutils/add-client-files.py
--- old/steam-launcher/buildutils/add-client-files.py   2024-02-08 
16:03:22.000000000 +0100
+++ new/steam-launcher/buildutils/add-client-files.py   2024-08-15 
19:36:26.000000000 +0200
@@ -184,11 +184,11 @@
                 strict=True,
             )
             runtimedir = os.path.join(tmpdir, 'client', 'ubuntu12_32')
-            client.download_runtime(datadir=runtimedir, strict=True)
-            client.extract_runtime(runtimedir=runtimedir, destdir=tmpdir)
+            client.download_scout(datadir=runtimedir, strict=True)
+            client.extract_scout(runtimedir=runtimedir, destdir=tmpdir)
 
             self.client_version = client.version
-            self.resolved_runtime = client.runtime_version
+            self.resolved_runtime = client.scout_version
 
         assert os.path.exists(
             os.path.join(tmpdir, 'client', 'steam.sh')
@@ -206,7 +206,7 @@
         previous scout build.
         """
         path = os.path.join(
-            tmpdir, 'client', 'ubuntu12_32', 'steam-runtime.tar.xz.part0',
+            tmpdir, 'client', 'ubuntu12_32', 'steam-runtime.tar.xz',
         )
 
         if self.runtime_version is None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/steam-launcher/buildutils/buildutils.py 
new/steam-launcher/buildutils/buildutils.py
--- old/steam-launcher/buildutils/buildutils.py 2024-02-08 16:03:22.000000000 
+0100
+++ new/steam-launcher/buildutils/buildutils.py 2024-08-15 19:36:26.000000000 
+0200
@@ -25,8 +25,12 @@
 import hashlib
 import io
 import logging
+import netrc
 import os
+import re
 import shutil
+import ssl
+import subprocess
 import tarfile
 import tempfile
 import typing
@@ -35,10 +39,8 @@
 import zipfile
 from contextlib import suppress
 
-import vdf
 
-
-logger = logging.getLogger('buildutils-lib')
+logger = logging.getLogger('runtimeutil')
 
 
 class HashingWriter(io.BufferedIOBase):
@@ -48,7 +50,6 @@
         self.size = 0
 
     def write(self, blob):
-        blob = bytes(blob)
         self.size += len(blob)
         self._sha256.update(blob)
         self._writer.write(blob)
@@ -65,19 +66,65 @@
         return self._writer.__exit__(*rest)
 
 
-def verbose_urlopen(
-    url     # type: str
-):
-    # type: (...) -> typing.BinaryIO
+def netrc_password_manager(
+    path: typing.Union[os.PathLike, str],
+) -> urllib.request.HTTPPasswordMgr:
+    """
+    Load a file in .netrc format from the given file.
+    Return a HTTPPasswordMgr that will present passwords from that file.
+    """
+    loader = netrc.netrc(str(path))
+
+    password_manager = urllib.request.HTTPPasswordMgrWithDefaultRealm()
+
+    for host in loader.hosts:
+        triple = loader.authenticators(host)
+
+        if triple is None:
+            continue
+
+        login, _, password = triple
+        password_manager.add_password(
+            None,       # type: ignore
+            'https://' + host,
+            login,
+            password,
+        )
+
+    return password_manager
+
 
+def verbose_urlopen(
+    url: str,
+    *,
+    opener: typing.Optional[urllib.request.OpenerDirector] = None,
+) -> typing.BinaryIO:
     logger.info('Requesting <%s>...', url)
     try:
-        return urllib.request.urlopen(url)
+        if opener is None:
+            return urllib.request.urlopen(url)
+        else:
+            return opener.open(url)
     except urllib.error.URLError:
         logger.error('Error opening <%s>:', url)
         raise
 
 
+def slugify(s: str) -> str:
+    """Convert s into a (possibly empty) string suitable for use in
+    filenames, URLs etc.
+    """
+    ret = []
+
+    for c in s:
+        if ord(c) < 128 and c.isalnum():
+            ret.append(c.lower())
+        else:
+            ret.append('-')
+
+    return re.sub(r'-+', '-', ''.join(ret).strip('-'))
+
+
 class SteamClient:
     def __init__(
         self,
@@ -91,9 +138,9 @@
         self.datetime = None        # type: typing.Optional[datetime.datetime]
         self.manifest_content = {
         }       # type: typing.Dict[str, typing.Dict[str, typing.Any]]
-        self.have_runtime_manifest = False
-        self.runtime_version = None             # type: typing.Optional[str]
-        self.runtime_version_marker = None      # type: typing.Optional[str]
+        self.have_scout_manifest = False
+        self.scout_version = None               # type: typing.Optional[str]
+        self.scout_version_marker = None        # type: typing.Optional[str]
 
     def download_manifest(
         self,
@@ -112,7 +159,9 @@
             with open(
                 os.path.join(datadir, 'manifest.vdf'), 'wb'
             ) as writer:
-                shutil.copyfileobj(http_reader, writer)
+                # mypy can't figure out that this is
+                # copyfileobj(BinaryIO -> BinaryIO)
+                shutil.copyfileobj(http_reader, writer)     # type: ignore
 
         self.load_manifest(datadir)
 
@@ -162,7 +211,9 @@
                                 value['size'], hasher.size)
 
                             if strict:
-                                raise ValueError('sha256 mismatch')
+                                raise ValueError(
+                                    'size mismatch (sha256 collision?!)'
+                                )
 
             for basename in names:
                 with zipfile.ZipFile(
@@ -172,23 +223,51 @@
                         part.filename = part.filename.replace('\\', '/')
                         unzip.extract(part, path=datadir)
 
-    def download_runtime(
+    def download_scout(
         self,
         datadir: str,
         strict: bool = False,
     ) -> None:
-        names = []      # type: typing.List[str]
+        self.download_runtime(1, datadir=datadir, strict=strict)
+
+    def download_runtime(
+        self,
+        major_version: typing.Union[int, str],
+        datadir: str,
+        strict: bool = False,
+    ) -> bool:
+        zip_name = ''
+        zip_part_names: typing.List[str] = []
+
+        suite = RuntimeArchive.CODENAMED_SUITE_VERSIONS.get(
+            str(major_version),
+            f'steamrt{major_version}',
+        )
 
         with tempfile.TemporaryDirectory() as tempdir:
             for name, value in sorted(
                 self.manifest_content['ubuntu12'].items(),
                 key=lambda i: i[0]
             ):
-                if (name.startswith('runtime_part')
-                        and name.endswith('_ubuntu12')):
+                is_whole = (name == f'runtime_{suite}_ubuntu12')
+                is_part = False
+
+                if suite == 'scout':
+                    is_part = (
+                        name.startswith('runtime_part')
+                        and name.endswith('_ubuntu12')
+                    )
+
+                if is_whole or is_part:
                     basename = value['file']
                     assert '/' not in basename, basename
-                    names.append(basename)
+
+                    if is_whole:
+                        assert not zip_name, (zip_name, basename)
+                        zip_name = basename
+                    else:
+                        zip_part_names.append(basename)
+
                     with verbose_urlopen(
                         '{}/{}'.format(self.uri, basename)
                     ) as downloader:
@@ -219,23 +298,63 @@
                                 if strict:
                                     raise ValueError('sha256 mismatch')
 
+            tar_name = {
+                'scout': 'steam-runtime.tar.xz',
+                'sniper': 'SteamLinuxRuntime_sniper.tar.xz',
+            }.get(suite, f'SteamLinuxRuntime_{major_version}.tar.xz')
+
+            look_for = {
+                'scout': [f'ubuntu12_32/{tar_name}'],
+                'sniper': [
+                    f'ubuntu12_64/{tar_name}',
+                    f'ubuntu12_64/steam-runtime-{suite}.tar.xz',
+                ],
+            }.get(suite, [f'ubuntu12_64/{tar_name}'])
+
+            found_parts = False
+
             with open(
-                os.path.join(datadir, 'steam-runtime.tar.xz'), 'wb'
+                os.path.join(datadir, tar_name), 'wb'
             ) as tar_writer:
-                for basename in names:
+                if zip_name:
+                    with zipfile.ZipFile(
+                        os.path.join(tempdir, zip_name), 'r'
+                    ) as unzip:
+                        for part in unzip.infolist():
+                            if part.filename.replace('\\', '/') in look_for:
+                                with unzip.open(part) as part_reader:
+                                    shutil.copyfileobj(
+                                        part_reader,
+                                        tar_writer
+                                    )       # type: ignore
+                                    return True
+
+                    raise AssertionError(f'{suite} not found in {zip_name}')
+
+                # Fallback: scout used to be shipped split into parts
+                for basename in zip_part_names:
                     with zipfile.ZipFile(
                         os.path.join(tempdir, basename), 'r'
                     ) as unzip:
                         for part in unzip.infolist():
                             if '.tar.xz.part' in part.filename:
                                 with unzip.open(part) as part_reader:
+                                    # mypy can't figure out that this
+                                    # is copyfileobj(BinaryIO -> BinaryIO)
                                     shutil.copyfileobj(
-                                        part_reader, tar_writer)
+                                        part_reader,
+                                        tar_writer
+                                    )       # type: ignore
+                                    found_parts = True
+
+            return found_parts
 
     def load_manifest(
         self,
         datadir: str,
     ) -> None:
+        import vdf
+
         with open(
             os.path.join(datadir, 'manifest.vdf'), 'r'
         ) as text_reader:
@@ -263,7 +382,7 @@
                 self.datetime.strftime('%Y-%m-%d %H:%M:%S%z'),
             )
 
-    def extract_runtime(
+    def extract_scout(
         self,
         runtimedir: str,
         destdir: str,
@@ -303,7 +422,7 @@
                     tar_reader.extract(info, path=destdir)
 
                     if info.name == 'steam-runtime/manifest.deb822.gz':
-                        self.have_runtime_manifest = True
+                        self.have_scout_manifest = True
 
                     continue
 
@@ -316,7 +435,7 @@
         ) as text_reader:
             marker = text_reader.read().strip()
 
-        self.runtime_version_marker = marker
+        self.scout_version_marker = marker
         version_bits = marker.split('_')
 
         if len(version_bits) != 2 or version_bits[0] != 'steam-runtime':
@@ -324,5 +443,411 @@
                 'Unexpected format for runtime version: %s', marker,
             )
         else:
-            self.runtime_version = version_bits[-1]
-            logger.info('Runtime version %s', self.runtime_version)
+            self.scout_version = version_bits[-1]
+            logger.info('scout runtime version %s', self.scout_version)
+
+    def get_container_runtime_version(
+        self,
+        major_version: typing.Union[int, str],
+        runtimedir: str,
+    ) -> typing.Optional[str]:
+        suite = RuntimeArchive.CODENAMED_SUITE_VERSIONS.get(
+            str(major_version),
+            f'steamrt{major_version}',
+        )
+
+        if suite == 'sniper':
+            top_dir = f'SteamLinuxRuntime_{suite}'
+        else:
+            top_dir = f'SteamLinuxRuntime_{major_version}'
+
+        with suppress(FileNotFoundError):
+            shutil.rmtree(os.path.join(runtimedir, top_dir))
+
+        with suppress(FileExistsError):
+            os.mkdir(os.path.join(runtimedir, top_dir))
+
+        candidates = (
+            top_dir + '.tar.xz',
+            f'steam-runtime-{suite}.tar.xz',
+        )
+
+        for candidate in candidates:
+            if os.path.exists(os.path.join(runtimedir, candidate)):
+                tarball = candidate
+                break
+
+            candidate = os.path.join('ubuntu12_64', candidate)
+
+            if os.path.exists(os.path.join(runtimedir, candidate)):
+                tarball = candidate
+                break
+        else:
+            logger.warning('One of %r not found', candidates)
+            return None
+
+        text = ''
+
+        # For whatever reason, the initial steam-runtime-sniper.tar.xz
+        # was actually bz2-compressed, so let tarfile auto-detect
+        with tarfile.open(
+            os.path.join(runtimedir, tarball),
+            mode='r:*',
+        ) as tar_reader:
+            for info in tar_reader:
+                if not info.isfile():
+                    continue
+
+                if info.name in (
+                    f'{top_dir}/VERSIONS.txt',
+                    f'steam-runtime-{suite}/VERSIONS.txt',
+                ):
+                    extractor = tar_reader.extractfile(info)
+                    assert extractor is not None
+
+                    with extractor as member_reader:
+                        text = member_reader.read().decode('utf-8')
+
+        for line in text.splitlines():
+            if line.startswith('depot\t'):
+                return line.split('\t')[1]
+            elif line.startswith(f'{suite}\t'):
+                return line.split('\t')[1]
+
+        return None
+
+
+class QuietError(Exception):
+    """
+    An error that usually doesn't provoke a traceback.
+    """
+
+
+def build_opener(
+    *,
+    handlers: typing.Iterable[urllib.request.BaseHandler] = [],
+    password_manager: typing.Optional[urllib.request.HTTPPasswordMgr] = None,
+    ssl_context: typing.Optional[ssl.SSLContext] = None
+) -> urllib.request.OpenerDirector:
+    hs = list(handlers)
+
+    if ssl_context is not None:
+        hs.append(urllib.request.HTTPSHandler(context=ssl_context))
+
+    if password_manager is not None:
+        hs.append(urllib.request.HTTPBasicAuthHandler(password_manager))
+
+    return urllib.request.build_opener(*hs)
+
+
+class PinnedRuntimeVersion:
+    def __init__(self, v: str, archive: 'RuntimeArchive') -> None:
+        if not v or not v[0].isdigit():
+            raise ValueError(
+                'Runtime version {!r} does not start with a digit'.format(v)
+            )
+
+        for c in v:
+            if c != '.' and not c.isdigit():
+                raise ValueError(
+                    'Runtime version {!r} contains non-dot, non-digit'.format(
+                        v
+                    )
+                )
+
+        self.version = v
+        self.archive = archive
+
+    def __eq__(self, other):
+        return (
+            isinstance(other, PinnedRuntimeVersion)
+            and self.version == other.version
+            and self.archive is other.archive
+        )
+
+    def __lt__(self, other):
+        if not isinstance(other, PinnedRuntimeVersion):
+            raise TypeError(
+                'Cannot compare {!r} with {!r}'.format(self, other)
+            )
+
+        if self.archive is not other.archive:
+            raise TypeError(
+                'Cannot compare {!r} with {!r}'.format(self, other)
+            )
+
+        return self.version < other.version
+
+    def __le__(self, other):
+        if not isinstance(other, PinnedRuntimeVersion):
+            raise TypeError(
+                'Cannot compare {!r} with {!r}'.format(self, other)
+            )
+
+        return self == other or self < other
+
+    def __gt__(self, other):
+        if not isinstance(other, PinnedRuntimeVersion):
+            raise TypeError(
+                'Cannot compare {!r} with {!r}'.format(self, other)
+            )
+
+        return other < self
+
+    def __ge__(self, other):
+        if not isinstance(other, PinnedRuntimeVersion):
+            raise TypeError(
+                'Cannot compare {!r} with {!r}'.format(self, other)
+            )
+
+        return self == other or other < self
+
+    def __hash__(self):
+        return hash(self.version)
+
+    def __str__(self) -> str:
+        return self.version
+
+    def __repr__(self) -> str:
+        return '<PinnedRuntimeVersion {!r} in {!r}>'.format(
+            self.version,
+            self.archive,
+        )
+
+    def get_uri(self, filename: str) -> str:
+        return self.archive.get_uri(self.version, filename)
+
+    def open(self, filename: str) -> typing.BinaryIO:
+        return self.archive.open(self.version, filename)
+
+    def fetch(
+        self,
+        filename: str,
+        destdir: str,
+        *,
+        log_level: typing.Optional[int] = None,
+        must_exist: bool = True
+    ) -> None:
+        self.archive.fetch(
+            self.version, filename, destdir,
+            log_level=log_level, must_exist=must_exist,
+        )
+
+
+class RuntimeArchive:
+    DEFAULT_IMAGES_URI = (
+        'https://repo.steampowered.com/IMAGES_DIR/snapshots'
+    )
+    DEFAULT_SSH_HOST = None     # type: typing.Optional[str]
+    DEFAULT_SSH_ROOT = None     # type: typing.Optional[str]
+
+    # Older branches of the Steam Runtime have a Team Fortress 2 character
+    # class as a codename. Newer branches are just called 'steamrt5' and
+    # so on.
+    SUITE_CODENAMES = {
+        'scout': '1',
+        'soldier': '2',
+        'sniper': '3',
+        'medic': '4',
+    }
+    CODENAMED_SUITE_VERSIONS = {
+        '1': 'scout',
+        '2': 'soldier',
+        '3': 'sniper',
+        '4': 'medic',
+    }
+
+    def __init__(
+        self,
+        suite: str,
+        *,
+        images_uri: typing.Optional[str] = None,
+        opener: typing.Optional[urllib.request.OpenerDirector] = None,
+        password_manager: typing.Optional[
+            urllib.request.HTTPPasswordMgr
+        ] = None,
+        ssh_host: typing.Optional[str] = None,
+        ssh_path: typing.Optional[str] = None,
+        ssh_root: typing.Optional[str] = None,
+        ssh_user: typing.Optional[str] = None,
+        ssl_context: typing.Optional[ssl.SSLContext] = None
+    ) -> None:
+        cls = self.__class__
+
+        self.suite = suite
+
+        if ssh_host is None:
+            ssh_host = cls.DEFAULT_SSH_HOST
+
+        if images_uri is None:
+            if suite in cls.SUITE_CODENAMES:
+                topdir = f'steamrt-{suite}'
+                images_dir = f'steamrt-images-{suite}'
+            else:
+                topdir = images_dir = suite
+
+            images_uri = (
+                cls.DEFAULT_IMAGES_URI
+            ).replace(
+                'SUITE', suite
+            ).replace(
+                'TOPDIR', topdir
+            ).replace(
+                'IMAGES_DIR', images_dir
+            )
+
+        self.images_uri = images_uri
+
+        if ssl_context is None:
+            ssl_context = cls.create_ssl_context()
+
+        if opener is None:
+            opener = build_opener(
+                password_manager=password_manager,
+                ssl_context=ssl_context,
+            )
+
+        self.opener = opener
+
+        if ssh_host is not None:
+            if ssh_root is None:
+                ssh_root = cls.DEFAULT_SSH_ROOT
+
+            if ssh_root is None:
+                ssh_root = f'/srv/{ssh_host}/www'
+
+            if ssh_path is None:
+                if suite in cls.SUITE_CODENAMES:
+                    ssh_path = f'steamrt-{suite}'
+                else:
+                    ssh_path = suite
+
+            if not ssh_path.startswith('/'):
+                ssh_path = f'{ssh_root}/{ssh_path}'
+
+            if ssh_user is None:
+                self.ssh_target = ssh_host      # type: typing.Optional[str]
+            else:
+                self.ssh_target = ssh_user + '@' + ssh_host
+        else:
+            self.ssh_target = None
+
+        self.ssh_path = ssh_path
+
+    @classmethod
+    def create_ssl_context(cls) -> ssl.SSLContext:
+        return ssl.create_default_context()
+
+    def __repr__(self) -> str:
+        return '<RuntimeArchive {!r}>'.format(self.suite)
+
+    def get_uri(
+        self,
+        version: str,
+        filename: str,
+    ) -> str:
+        return '{}/{}/{}'.format(self.images_uri, version, filename)
+
+    def open(
+        self,
+        version: str,
+        filename: str,
+        *,
+        log_level: int = logging.ERROR,
+    ) -> typing.BinaryIO:
+        """
+        Open and stream the given file in the given version of this
+        runtime. Unlike fetch(), this always uses http or https.
+
+        If we cannot open it, log a message at level log_level and reraise
+        the exception.
+        """
+        uri = self.get_uri(version, filename)
+
+        logger.info('Requesting <%s>...', uri)
+        try:
+            return self.opener.open(uri)
+        except urllib.error.URLError:
+            if log_level > logging.NOTSET:
+                logger.log(log_level, 'Error opening <%s>:', uri)
+            raise
+
+    def pin_version(
+        self,
+        version: str,
+        *,
+        log_level: int = logging.ERROR,
+    ) -> PinnedRuntimeVersion:
+        """
+        Get the "pinned" version corresponding to the given
+        symbolic version.
+        If it's a version number rather than a symbolic version,
+        just return it as a PinnedRuntimeVersion.
+        """
+        with self.open(
+            version, 'VERSION.txt', log_level=log_level
+        ) as http_reader:
+            return PinnedRuntimeVersion(
+                http_reader.read().decode('ascii').strip(),
+                self,
+            )
+
+    def fetch(
+        self,
+        version: str,
+        filename: str,
+        destdir: str,
+        *,
+        log_level: typing.Optional[int] = None,
+        must_exist: bool = True,
+        rsync: bool = True,
+    ) -> None:
+        """
+        Download the given file from the given version of this runtime.
+        Write it to a file of the same basename in destdir.
+
+        Use rsync for an incremental transfer if possible, unless @rsync
+        is false.
+        """
+        ssh_target = self.ssh_target
+        ssh_path = self.ssh_path
+
+        if log_level is None:
+            if must_exist:
+                log_level = logging.ERROR
+            else:
+                log_level = logging.INFO
+
+        if (
+            ssh_target is not None
+            and ssh_path is not None
+            and must_exist
+            and rsync
+        ):
+            path = f'{ssh_path}/{version}/{filename}'
+            logger.info('Downloading %r...', path)
+            subprocess.run([
+                'rsync',
+                '--archive',
+                '--partial',
+                '--progress',
+                ssh_target + ':' + path,
+                os.path.join(destdir, filename),
+            ], check=True)
+        else:
+            try:
+                with self.open(
+                    version, filename,
+                    log_level=log_level,
+                ) as response, open(
+                    os.path.join(destdir, filename), 'wb',
+                ) as writer:
+                    # mypy can't figure out that this is
+                    # copyfileobj(BinaryIO -> BinaryIO)
+                    shutil.copyfileobj(response, writer)    # type: ignore
+            except urllib.error.URLError:
+                with suppress(FileNotFoundError):
+                    os.remove(os.path.join(destdir, filename))
+
+                if must_exist:
+                    raise
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/steam-launcher/client-versions.json 
new/steam-launcher/client-versions.json
--- old/steam-launcher/client-versions.json     2024-02-08 16:05:56.000000000 
+0100
+++ new/steam-launcher/client-versions.json     2024-08-15 19:38:55.000000000 
+0200
@@ -1,4 +1,4 @@
 {
-  "client_version": "1705108172",
-  "runtime_version": "0.20231127.68515"
+  "client_version": "1721173382",
+  "runtime_version": "0.20240610.91380"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/steam-launcher/com.valvesoftware.Steam.metainfo.xml 
new/steam-launcher/com.valvesoftware.Steam.metainfo.xml
--- old/steam-launcher/com.valvesoftware.Steam.metainfo.xml     2024-02-08 
16:03:22.000000000 +0100
+++ new/steam-launcher/com.valvesoftware.Steam.metainfo.xml     2024-08-15 
19:36:26.000000000 +0200
@@ -32,6 +32,8 @@
   </screenshots>
   <launchable type="desktop-id">steam.desktop</launchable>
   <releases>
+    <release version="1.0.0.81" date="2024-08-15"/>
+    <release version="1.0.0.80" date="2024-04-22"/>
     <release version="1.0.0.79" date="2024-02-08"/>
     <release version="1.0.0.78" date="2023-05-09"/>
     <release version="1.0.0.77" date="2023-05-09"/>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/steam-launcher/debian/README.source 
new/steam-launcher/debian/README.source
--- old/steam-launcher/debian/README.source     2024-02-08 16:03:22.000000000 
+0100
+++ new/steam-launcher/debian/README.source     1970-01-01 01:00:00.000000000 
+0100
@@ -1,124 +0,0 @@
-Notes for maintainers
-=====================
-
-Incorporating binaries from the Steam client
---------------------------------------------
-
-The bootstrap tarball contains a small subset of the Steam client.
-It is not maintained in this repository, and is ignored by our .gitignore.
-
-To fetch it, run buildutils/add-client-files.py. By default, it will
-download files from the latest public general-availability Steam client.
-There are many options: run buildutils/add-client-files.py --help for
-an up-to-date list.
-
-Use --client-manifest=steam_client_publicbeta_ubuntu12 if you would prefer
-to update the bootstrapper from the latest public beta Steam client.
-
---client-dir=/path/to/client (or --client-dir=/path/to/.steam/steam) gets
-the client files directly from a Steam client installation.
-
---client-tarball-uri gets the client files from a tarball, which must
-have a top-level directory (its name is ignored) containing filenames
-like `steam.sh` and `ubuntu12_32`, for example:
-
-    client-2020-02-24/
-        steam.sh
-        ubuntu12_32/
-            steam
-            steam-runtime.tar.xz.part0
-            ...
-        ...
-
-(For details of the minimal files that must be present in the tarball,
-see the source code!)
-
-You can specify an option like --runtime-version=0.20200204.0 to delete the
-Steam Runtime from the client and replace it with a suitable Steam Runtime
-before packing the bootstrap tarball. In particular, you must provide this
-option if the runtime is missing from your client.
-
-Updating subprojects
---------------------
-
-To update the udev rules from steam-devices:
-
-    # only necessary the first time
-    git remote add --no-tags steam-devices 
https://github.com/ValveSoftware/steam-devices
-    # merge the upstream changes into our subtree
-    git subtree merge -P subprojects/steam-devices steam-devices/master
-    # review the changes
-    git diff HEAD~
-
-Updating PGP keys
------------------
-
-Historically `steam.gpg` was regenerated with:
-
-    gpg \
-    --homedir=$(CURDIR)/gpg \
-    --no-default-keyring \
-    --keyring=$(CURDIR)/steam.gpg \
-    --import steam-key.asc
-
-but that's probably only valid for GPG v1. In future (if we generate
-new keys or uids, or update revocation dates) it would be better to use:
-
-    gpg --export-options export-clean,export-minimal --export KEYIDS... > 
steam.gpg
-    gpg --armor --export-options export-clean,export-minimal --export 
KEYIDS... > steam-key.asc
-
-on a machine that holds the canonical version of the keys.
-
-Preparing a beta release
-------------------------
-
-* Check that the diff is appropriate
-
-* Download the prerelease package produced by Gitlab-CI (each commit
-    to git master generates a package in the `playground/steam-launcher`
-    beta suite), and test it on whatever distributions are appropriate
-
-* Update `debian/changelog` to include all the latest changes, if not
-    already done (`gbp dch --full` from the `git-buildpackage` package
-    might help)
-
-* Finalize the first line of the changelog, and the following
-    "sign-off" line starting with ` --`
-    (`debchange -r` from the `devscripts` package might help)
-
-* Update the `STEAMSCRIPT_VERSION` in `bin_steam.sh`
-
-* Add a `<release>` in `com.valvesoftware.Steam.metainfo.xml`, preserving
-    newest-first order
-
-* Commit the changes
-
-* `git tag -m 'steam-launcher vX.Y.Z.W' -a vX.Y.Z.W`
-
-* `git push origin master vX.Y.Z.W`
-
-* If all goes well, the new release will be built automatically from the
-    `vX.Y.Z.W` tag by Gitlab-CI, and go into the internal staging apt
-    repository (`steam-launcher` beta suite).
-
-* Last chance to test! If there's a problem, fix it and start again with
-    a new version number.
-
-* Trigger the Gitlab-CI pipeline on the default branch with
-    the `STEAM_LAUNCHER_CI_ACTION` CI variable set to `public`.
-    This will copy the staging beta to be the public beta
-    (and also the staging stable to the public stable).
-
-Putting a beta release into production
---------------------------------------
-
-* Trigger the Gitlab-CI pipeline on the default branch with
-    the `STEAM_LAUNCHER_CI_ACTION` CI variable set to `staging-stable`.
-    This will copy the staging beta to staging stable.
-
-* *Really* the last chance to test :-)
-
-* Trigger the Gitlab-CI pipeline on the default branch with
-    the `STEAM_LAUNCHER_CI_ACTION` CI variable set to `public`.
-    This will copy the staging stable to be the public stable
-    (and also the staging beta to the public beta).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/steam-launcher/debian/changelog 
new/steam-launcher/debian/changelog
--- old/steam-launcher/debian/changelog 2024-02-08 16:03:22.000000000 +0100
+++ new/steam-launcher/debian/changelog 2024-08-15 19:36:26.000000000 +0200
@@ -1,3 +1,29 @@
+steam (1:1.0.0.81) beta; urgency=medium
+
+  * bin_steam.sh: Optimize the case where Steam is already running,
+    by using steam-runtime-steam-remote (if available) to forward the
+    command-line to the running instance (steamrt/tasks#496)
+  * bin_steam.sh: Don't copy bootstrap.tar.xz if it already matches
+    (steamrt/tasks#496)
+  * Build using updated Steam client:
+    - Client timestamp 1721173382 (2024-07-16)
+    - Steam Runtime (scout) version 0.20240610.91380
+  * Update steam-devices subproject up to 2024-05-22:
+    - Performance Designed Products Victrix Pro FS-12
+      (steam-devices#47, thanks to @notpentadactyl)
+  * buildutil: Internal changes (steamrt/tasks#468)
+  * CI: Make tests more realistic
+
+ -- Simon McVittie <s...@collabora.com>  Thu, 15 Aug 2024 18:36:00 +0100
+
+steam (1:1.0.0.80) beta; urgency=medium
+
+  * Build using updated Steam client:
+    - Client timestamp 1709846872 (2024-03-07)
+    - Steam Runtime (scout) version 0.20240304.79797
+
+ -- Simon McVittie <s...@collabora.com>  Mon, 22 Apr 2024 15:08:05 +0100
+
 steam (1:1.0.0.79) beta; urgency=medium
 
   [ Timothee Besset ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/steam-launcher/debian/source/options 
new/steam-launcher/debian/source/options
--- old/steam-launcher/debian/source/options    2024-02-08 16:03:22.000000000 
+0100
+++ new/steam-launcher/debian/source/options    2024-08-15 19:36:26.000000000 
+0200
@@ -1,5 +1,3 @@
 tar-ignore=.git
-tar-ignore=.gitlab-ci.yml
 tar-ignore=.mypy_cache
 tar-ignore=__pycache__
-tar-ignore=ci
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/steam-launcher/subprojects/steam-devices/60-steam-input.rules 
new/steam-launcher/subprojects/steam-devices/60-steam-input.rules
--- old/steam-launcher/subprojects/steam-devices/60-steam-input.rules   
2024-02-08 16:03:22.000000000 +0100
+++ new/steam-launcher/subprojects/steam-devices/60-steam-input.rules   
2024-08-15 19:36:26.000000000 +0200
@@ -137,3 +137,6 @@
 
 # Thrustmaster eSwap Pro
 KERNEL=="hidraw*", ATTRS{idVendor}=="044f", ATTRS{idProduct}=="d00e", 
MODE="0660", TAG+="uaccess"
+
+# Performance Designed Products Victrix Pro FS-12 for PS4 & PS5
+KERNEL=="hidraw*", ATTRS{idVendor}=="0e6f", ATTRS{idProduct}=="020c", 
MODE="0660", TAG+="uaccess"

Reply via email to