Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:

strategy:
matrix:
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13", "3.14" ]
os: [ ubuntu-latest, macos-latest, windows-latest ]

steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
Expand Down
21 changes: 8 additions & 13 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
repos:
- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.9.3
hooks:
- id: isort
args: [ '-m', 'HANGING_INDENT', '-l', '120','--check-only' ]
files: \.py$

- repo: https://github.com/pycqa/flake8
rev: "7.0.0"
hooks:
- id: flake8
args: [ '--count', '--select=E9,F63,F7,F82,F401,E741', '--show-source', '--statistics', '--max-complexity=10', '--max-line-length=127' ]
files: \.py$
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.2
hooks:
# Run the linter.
- id: ruff-check
# Run the formatter.
- id: ruff-format
68 changes: 36 additions & 32 deletions ipsw_parser/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,84 +12,88 @@

coloredlogs.install(level=logging.DEBUG)

logging.getLogger('asyncio').disabled = True
logging.getLogger('parso.cache').disabled = True
logging.getLogger('parso.cache.pickle').disabled = True
logging.getLogger('parso.python.diff').disabled = True
logging.getLogger('humanfriendly.prompts').disabled = True
logging.getLogger('blib2to3.pgen2.driver').disabled = True
logging.getLogger('urllib3.connectionpool').disabled = True
logging.getLogger("asyncio").disabled = True
logging.getLogger("parso.cache").disabled = True
logging.getLogger("parso.cache.pickle").disabled = True
logging.getLogger("parso.python.diff").disabled = True
logging.getLogger("humanfriendly.prompts").disabled = True
logging.getLogger("blib2to3.pgen2.driver").disabled = True
logging.getLogger("urllib3.connectionpool").disabled = True

logger = logging.getLogger(__name__)

PEM_DB_ENV_VAR = 'IPSW_PARSER_PEM_DB'
PEM_DB_ENV_VAR = "IPSW_PARSER_PEM_DB"


def handle_ipsw_argument(ctx: click.Context, param: click.Argument, value: str) -> IPSW:
if value.startswith('http://') or value.startswith('https://'):
if value.startswith("http://") or value.startswith("https://"):
return IPSW(RemoteZip(value))
return IPSW(ZipFile(Path(value).expanduser()))


ipsw_argument = click.argument('ipsw', callback=handle_ipsw_argument)
pem_db_option = click.option('--pem-db', envvar=PEM_DB_ENV_VAR,
help='Path DB file url (can be either a filesystem path or an HTTP URL). '
'Alternatively, use the IPSW_PARSER_PEM_DB envvar.')
ipsw_argument = click.argument("ipsw", callback=handle_ipsw_argument)
pem_db_option = click.option(
"--pem-db",
envvar=PEM_DB_ENV_VAR,
help="Path DB file url (can be either a filesystem path or an HTTP URL). "
"Alternatively, use the IPSW_PARSER_PEM_DB envvar.",
)


@click.group()
def cli() -> None:
""" CLI utility for extracting info from IPSW files """
"""CLI utility for extracting info from IPSW files"""
pass


@cli.command('info')
@cli.command("info")
@ipsw_argument
def info(ipsw) -> None:
""" Parse given .ipsw basic info """
print(f'SupportedProductTypes: {ipsw.build_manifest.supported_product_types}')
print(f'ProductVersion: {ipsw.build_manifest.product_version}')
print(f'ProductBuildVersion: {ipsw.build_manifest.product_build_version}')
"""Parse given .ipsw basic info"""
print(f"SupportedProductTypes: {ipsw.build_manifest.supported_product_types}")
print(f"ProductVersion: {ipsw.build_manifest.product_version}")
print(f"ProductBuildVersion: {ipsw.build_manifest.product_build_version}")

development_files = ipsw.get_development_files()
if development_files:
print('DevelopmentFiles:')
print("DevelopmentFiles:")
for file in development_files:
print(f'- {file}')
print(f"- {file}")


@cli.command('extract')
@cli.command("extract")
@ipsw_argument
@click.argument('output', type=click.Path(exists=False))
@click.argument("output", type=click.Path(exists=False))
@pem_db_option
def extract(ipsw: IPSW, output: str, pem_db: Optional[str]) -> None:
""" Extract .ipsw into filesystem layout """
"""Extract .ipsw into filesystem layout"""
output = Path(output)

if not output.exists():
output.mkdir(parents=True, exist_ok=True)

ipsw.build_manifest.build_identities[0].extract(output, pem_db=pem_db)
ipsw.archive.extractall(
path=output, members=[f for f in ipsw.archive.filelist if f.filename.startswith('Firmware')])
path=output, members=[f for f in ipsw.archive.filelist if f.filename.startswith("Firmware")]
)


@cli.command('extract-kernel')
@cli.command("extract-kernel")
@ipsw_argument
@click.argument('output', type=click.Path(exists=False))
@click.option('--arch', help='Arch name to extract using lipo')
@click.argument("output", type=click.Path(exists=False))
@click.option("--arch", help="Arch name to extract using lipo")
def extract_kernel(ipsw: IPSW, output: str, arch: Optional[str]) -> None:
""" Extract kernelcache from given .ipsw into given output filename """
"""Extract kernelcache from given .ipsw into given output filename"""
Path(output).write_bytes(ipsw.build_manifest.build_identities[0].get_kernelcache_payload(arch=arch))


@cli.command('device-support')
@cli.command("device-support")
@ipsw_argument
@pem_db_option
def device_support(ipsw: IPSW, pem_db: Optional[str]) -> None:
""" Create DeviceSupport directory """
"""Create DeviceSupport directory"""
ipsw.create_device_support(pem_db=pem_db)


if __name__ == '__main__':
if __name__ == "__main__":
cli()
Loading