Skip to content

Commit

Permalink
Add support for large iOS application packages (#342)
Browse files Browse the repository at this point in the history
  • Loading branch information
priitlatt authored Sep 4, 2023
1 parent 6fa250a commit 45dfff5
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 7 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
Version 0.42.2
-------------

**Bugfixes**
- Fix iOS application package abstraction layer in `codemagic.models.application_package.Ipa` to support large archives (exceeding 4GB in size). [PR #342](https://github.com/codemagic-ci-cd/cli-tools/pull/342)

Version 0.42.1
-------------

**Bugfixes**
- Do not require certificate private key to show certificate information using `app-store-connect get-certificate` if certificate is not saved to disk. [PR #XYZ](https://github.com/codemagic-ci-cd/cli-tools/pull/XYZ)
- Do not require certificate private key to show certificate information using `app-store-connect get-certificate` if certificate is not saved to disk. [PR #337](https://github.com/codemagic-ci-cd/cli-tools/pull/337)

Version 0.42.0
-------------
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "codemagic-cli-tools"
version = "0.42.1"
version = "0.42.2"
description = "CLI tools used in Codemagic builds"
readme = "README.md"
authors = [
Expand Down
2 changes: 1 addition & 1 deletion src/codemagic/__version__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__title__ = "codemagic-cli-tools"
__description__ = "CLI tools used in Codemagic builds"
__version__ = "0.42.1.dev"
__version__ = "0.42.2.dev"
__url__ = "https://github.com/codemagic-ci-cd/cli-tools"
__licence__ = "GNU General Public License v3.0"
22 changes: 18 additions & 4 deletions src/codemagic/models/application_package/ipa.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pathlib
import plistlib
import shutil
import subprocess
import zipfile
from functools import lru_cache
Expand All @@ -8,6 +9,7 @@
from typing import Dict
from typing import List
from typing import Optional
from typing import Tuple
from typing import Union

from codemagic.models.certificate import Certificate
Expand All @@ -21,8 +23,8 @@ class Ipa(AbstractPackage):
def _validate_package(self):
try:
return bool(self.info_plist)
except zipfile.BadZipFile as bad_zip_file:
raise IOError(f"Not a valid iOS application package at {self.path}") from bad_zip_file
except (subprocess.CalledProcessError, zipfile.BadZipFile) as error:
raise IOError(f"Not a valid iOS application package at {self.path}") from error

def _extract_file(self, filename_filter: Callable[[str], bool]) -> bytes:
with zipfile.ZipFile(self.path) as zf:
Expand All @@ -35,14 +37,26 @@ def _extract_file(self, filename_filter: Callable[[str], bool]) -> bytes:
with zf.open(found_file_name, "r") as fd:
return fd.read()
except zipfile.BadZipFile as e:
extract_command: Tuple[Union[str, pathlib.Path], ...]

if str(e) == "Bad magic number for file header":
# Big ipas are compressed using lzfse compression format which adds extra bytes to Info-Zip
# Unfortunately Python's built-in zip library is not capable to handle those
pass
extract_command = ("unzip", "-p", self.path, found_file_name)
elif str(e) == "Truncated file header":
# If the archive size exceeds 4GB then macOS created "corrupt" zip files as it doesn't
# use zip64 specification. 7-Zip is capable of extracting such archives. Let's try that.
if not shutil.which("7z"):
error_message = (
f"Failed to inspect iOS application package at {self.path}. "
f"Please ensure 7-Zip is installed and 7z executable in available in $PATH."
)
raise IOError(error_message) from e
extract_command = ("7z", "x", "-so", self.path, found_file_name)
else:
raise

completed_process = subprocess.run(["unzip", "-p", self.path, found_file_name], capture_output=True)
completed_process = subprocess.run(extract_command, capture_output=True, check=True)
return completed_process.stdout

def _get_app_file_contents(self, filename: str) -> bytes:
Expand Down

0 comments on commit 45dfff5

Please sign in to comment.