From f6e8a72a890fe8b40339bb447a55e0c9963ee7e0 Mon Sep 17 00:00:00 2001 From: Yannic Bonenberger Date: Mon, 20 Apr 2026 10:52:53 +0200 Subject: [PATCH] [deb] Avoid `UTF-8` roundtrip for `{pre,post}-{inst,rm}` hooks This change allows the Debian package hooks to be arbitrary files (including executable binary files), which failed with the following error before: ``` Traceback (most recent call last): File "/Users/yannic/Library/Caches/bazel/_bazel_yannic/bb6c3b02ccc98aaca966df445b5024eb/sandbox/darwin-sandbox/3472/execroot/_main/bazel-out/darwin_x86_64-opt-exec/bin/external/rules_pkg+/pkg/private/deb/make_deb.runfiles/rules_pkg+/pkg/private/deb/_make_deb_stage2_bootstrap.py", line 499, in main() ~~~~^^ File "/Users/yannic/Library/Caches/bazel/_bazel_yannic/bb6c3b02ccc98aaca966df445b5024eb/sandbox/darwin-sandbox/3472/execroot/_main/bazel-out/darwin_x86_64-opt-exec/bin/external/rules_pkg+/pkg/private/deb/make_deb.runfiles/rules_pkg+/pkg/private/deb/_make_deb_stage2_bootstrap.py", line 493, in main _run_py_path(main_filename, args=sys.argv[1:]) ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/yannic/Library/Caches/bazel/_bazel_yannic/bb6c3b02ccc98aaca966df445b5024eb/sandbox/darwin-sandbox/3472/execroot/_main/bazel-out/darwin_x86_64-opt-exec/bin/external/rules_pkg+/pkg/private/deb/make_deb.runfiles/rules_pkg+/pkg/private/deb/_make_deb_stage2_bootstrap.py", line 287, in _run_py_path runpy.run_path(main_filename, run_name="__main__") ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "", line 287, in run_path File "", line 98, in _run_module_code File "", line 88, in _run_code File "/Users/yannic/Library/Caches/bazel/_bazel_yannic/bb6c3b02ccc98aaca966df445b5024eb/sandbox/darwin-sandbox/3472/execroot/_main/bazel-out/darwin_x86_64-opt-exec/bin/external/rules_pkg+/pkg/private/deb/make_deb.runfiles/rules_pkg+/pkg/private/deb/make_deb.py", line 433, in main() ~~~~^^ File "/Users/yannic/Library/Caches/bazel/_bazel_yannic/bb6c3b02ccc98aaca966df445b5024eb/sandbox/darwin-sandbox/3472/execroot/_main/bazel-out/darwin_x86_64-opt-exec/bin/external/rules_pkg+/pkg/private/deb/make_deb.runfiles/rules_pkg+/pkg/private/deb/make_deb.py", line 392, in main prerm=helpers.GetFlagValue(options.prerm, False), ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^ File "/Users/yannic/Library/Caches/bazel/_bazel_yannic/bb6c3b02ccc98aaca966df445b5024eb/sandbox/darwin-sandbox/3472/execroot/_main/bazel-out/darwin_x86_64-opt-exec/bin/external/rules_pkg+/pkg/private/deb/make_deb.runfiles/rules_pkg+/pkg/private/helpers.py", line 81, in GetFlagValue flagvalue = f.read().decode('utf-8') UnicodeDecodeError: 'utf-8' codec can't decode byte 0xcf in position 0: invalid continuation byte ``` (the example used a `go_binary` for `prerm`) --- pkg/private/deb/make_deb.py | 5 +++- pkg/private/helpers.py | 50 ++++++++++++++++++++++++------------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/pkg/private/deb/make_deb.py b/pkg/private/deb/make_deb.py index ee9bdc38..967b75b5 100644 --- a/pkg/private/deb/make_deb.py +++ b/pkg/private/deb/make_deb.py @@ -180,7 +180,10 @@ def CreateDebControl(extrafiles=None, **kwargs): if extrafiles: for name, (data, mode) in extrafiles.items(): tarinfo = tarfile.TarInfo('./' + name) - data_encoded = data.encode('utf-8') + if type(data) == type(b''): + data_encoded = data + else: + data_encoded = data.encode('utf-8') tarinfo.size = len(data_encoded) tarinfo.mode = mode f.addfile(tarinfo, fileobj=io.BytesIO(data_encoded)) diff --git a/pkg/private/helpers.py b/pkg/private/helpers.py index 5147cc2f..c503eb30 100644 --- a/pkg/private/helpers.py +++ b/pkg/private/helpers.py @@ -48,7 +48,7 @@ def SplitNameValuePairAtSeparator(arg, sep): # if we leave the loop, the character sep was not found unquoted return (head, '') -def GetFlagValue(flagvalue, strip=True): +def GetFlagValue(flagvalue, strip=True, encoding='utf-8'): """Converts a raw flag string to a useable value. 1. Expand @filename style flags to the content of filename. @@ -68,22 +68,36 @@ def GetFlagValue(flagvalue, strip=True): Python2: unicode Python3: str """ - if flagvalue: - if sys.version_info[0] < 3: - # python2 gives us raw bytes in argv. - flagvalue = flagvalue.decode('utf-8') - # assertion: py2: flagvalue is unicode - # assertion: py3: flagvalue is str, but in weird format - if flagvalue[0] == '@': - # Subtle: We do not want to re-encode the value here, because it - # is encoded in the right format for file open operations. - with open(flagvalue[1:], 'rb') as f: - flagvalue = f.read().decode('utf-8') + + if flagvalue is None: + return None + + if flagvalue and (sys.version_info[0] < 3): + # python2 gives us raw bytes in argv. + flagvalue = flagvalue.decode('utf-8') + + # assertion: py2: flagvalue is unicode + # assertion: py3: flagvalue is str, but in weird format + + if not flagvalue: + data = b'' + elif flagvalue[0] == '@': + with open(flagvalue[1:], 'rb') as f: + data = f.read() + else: + # Convert fs specific encoding back to proper unicode. + if sys.version_info[0] > 2: + data = os.fsencode(flagvalue) else: - # convert fs specific encoding back to proper unicode. - if sys.version_info[0] > 2: - flagvalue = os.fsencode(flagvalue).decode('utf-8') + data = flagvalue.encode('utf-8') + + # assertion: data is byte array. + + if not encoding: + return data + + value = data.decode(encoding) - if strip: - return flagvalue.strip() - return flagvalue + if strip: + return value.strip() + return value