├── .gitignore ├── Makefile ├── README.md ├── ipa_deploy ├── __init__.py └── version.py ├── pypi.sh └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore following files 2 | .DS_Store 3 | .settings 4 | .idea 5 | .pytest_cache 6 | __pycache__ 7 | *.iml 8 | ipa_deploy.egg-info 9 | build 10 | dist 11 | log 12 | tmp 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | test: 3 | pytest 4 | 5 | version: 6 | python3 ipa_deploy/version.py bump 7 | 8 | clean: 9 | rm -rf dist/* 10 | rm -rf build/* 11 | 12 | package: 13 | rm -rf dist/* 14 | python3 setup.py sdist bdist_wheel 15 | ls -la dist/ 16 | 17 | publishtest: 18 | twine upload --repository testpypi dist/* --verbose 19 | 20 | publish: 21 | twine upload --repository pypi dist/* --verbose 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Command line tool to deploy IPA package to iOS device. 3 | 4 | It was written with nodejs before, now it's re-written with python3. 5 | 6 | # Installation # 7 | 8 | Please make sure your Python is v3.7 or above. 9 | 10 | ```bash 11 | python3 --version 12 | python3 -m pip install ipa-deploy 13 | ``` 14 | 15 | Or, clone from GitHub: 16 | ```bash 17 | git clone https://github.com/floatinghotpot/ipa-deploy.git 18 | cd ipa-deploy 19 | python3 -m pip install -e . 20 | ``` 21 | 22 | # How To Use # 23 | 24 | ```bash 25 | ipa-deploy 26 | ``` 27 | 28 | Example: 29 | ```bash 30 | ipa-deploy myapp.ipa 31 | ``` 32 | 33 | # How It Works # 34 | 35 | Here are the steps that the tool actualy runs: 36 | 37 | ```bash 38 | # unzip the IPA file to tmp folder 39 | mkdir ./tmp 40 | unzip -d ./tmp 41 | 42 | # run ios-deploy to install the app into iOS device 43 | ios-deploy -b ./tmp/Payload/*.app 44 | 45 | # clean up the tmp folder 46 | rm -r ./tmp 47 | ``` 48 | 49 | # Dependency # 50 | 51 | It will call [ios-deploy](https://github.com/ios-control/ios-deploy), so make sure it's installed first. 52 | 53 | If not installed, install it with Homebrew: 54 | ```bash 55 | brew install ios-deploy 56 | ``` 57 | 58 | # Credits # 59 | 60 | A simple tool created by Raymond Xie, to install IPA package with command line. 61 | 62 | Any comments are welcome. 63 | -------------------------------------------------------------------------------- /ipa_deploy/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8; py-indent-offset:4 -*- 3 | from __future__ import (absolute_import, division, print_function, 4 | unicode_literals) 5 | 6 | import sys, os, subprocess 7 | from .version import __version__ 8 | 9 | sys.path.append(os.getcwd()) 10 | 11 | def exec_cmd(cmd, capture_output= False): 12 | #print(cmd) 13 | ret = subprocess.run( cmd, shell=True, capture_output= capture_output ) 14 | return ret.returncode, ret.stdout.decode() if capture_output else '' 15 | 16 | def cli_main_help(): 17 | syntax_tips = '''Syntax: 18 | __argv0__ 19 | 20 | Example: 21 | __argv0__ app.ipa 22 | '''.replace('__argv0__',os.path.basename(sys.argv[0])) 23 | 24 | print(syntax_tips) 25 | 26 | def parse_params_options(argv): 27 | params = [] 28 | options = [] 29 | for i in range(1, len(argv)): 30 | str = argv[i] 31 | if str[0] == '-': 32 | options.append(str) 33 | else: 34 | params.append(str) 35 | 36 | return params, options 37 | 38 | def confirm_installed( bin ): 39 | status, output = exec_cmd( 'which ' + bin ) 40 | print(status, output) 41 | pass 42 | 43 | def cli_main_params_options(params, options): 44 | if ('-v' in options) or ('--version' in options): 45 | print( __version__ ) 46 | return 47 | 48 | if len(params) == 0: 49 | cli_main_help() 50 | return 51 | 52 | # confirm the IPA file exists 53 | ipa_file = os.path.abspath(params[0]) 54 | if not os.path.exists(ipa_file): 55 | print('Error: ' + ipa_file + ' not exists') 56 | return 57 | 58 | # confirm the commands installed 59 | for bin in ['unzip', 'ios-deploy']: 60 | status, output = exec_cmd( 'which ' + bin, capture_output= True ) 61 | if status != 0: 62 | print('Error: ' + bin + ' not found') 63 | if bin == 'ios-deploy': 64 | print('Please install ios-deploy via Homebrew by running:') 65 | print(' brew install ios-deploy') 66 | return 67 | 68 | # exec the commands 69 | cmds = [ 70 | 'rm -rf ./tmp', 71 | 'mkdir ./tmp', 72 | 'unzip "' + ipa_file + '" -d ./tmp', 73 | 'ios-deploy -b ./tmp/Payload/*.app', 74 | 'rm -rf ./tmp', 75 | ] 76 | for cmd in cmds: 77 | status, output = exec_cmd( cmd ) 78 | if status != 0: 79 | print('Error: failed exec "' + cmd + '"') 80 | return 81 | 82 | def cli_main(): 83 | params, options = parse_params_options(sys.argv) 84 | cli_main_params_options(params, options) 85 | 86 | if __name__ == "__main__": 87 | cli_main() 88 | -------------------------------------------------------------------------------- /ipa_deploy/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; py-indent-offset:4 -*- 2 | from __future__ import (absolute_import, division, print_function, unicode_literals) 3 | 4 | import os 5 | import sys 6 | 7 | __version__ = '2.0.1' 8 | 9 | __hqversion__ = tuple(int(x) for x in __version__.split('.')) 10 | 11 | if __name__ == "__main__": 12 | if len(sys.argv) > 1 and sys.argv[1] == 'bump': 13 | fp = open(__file__, 'r') 14 | lines = fp.readlines() 15 | fp.close() 16 | 17 | text = '' 18 | for line in lines: 19 | if line.startswith('__version__'): 20 | items = line.split("'") 21 | current_version = items[1] 22 | vers = items[1].split('.') 23 | vers[-1] = str(int(vers[-1]) +1) 24 | new_version = items[1] = '.'.join(vers) 25 | line = "'".join(items) 26 | text += line 27 | 28 | fp = open(__file__, 'w') 29 | fp.write(text) 30 | fp.close() 31 | print('Current version:', current_version) 32 | print('Bumped to:', new_version) 33 | print('File updated:', __file__, '\n') 34 | -------------------------------------------------------------------------------- /pypi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Generate pypi wheels universal package and upload 4 | # 5 | python3 setup.py bdist_wheel --universal upload -r pypi 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8; py-indent-offset:4 -*- 3 | ############################################################################### 4 | # 5 | # Copyright (C) 2022 Liming Xie 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | ############################################################################### 21 | import os.path 22 | import codecs # To use a consistent encoding 23 | import setuptools 24 | 25 | here = os.path.abspath(os.path.dirname(__file__)) 26 | 27 | # Get the long description from the relevant file 28 | with codecs.open(os.path.join(here, 'README.md'), encoding='utf-8') as f: 29 | long_description = f.read() 30 | 31 | #from ipa-deploy.version import __version__ 32 | lines = open(here + '/ipa_deploy/version.py', mode='r').readlines() 33 | for line in lines: 34 | if line.startswith('__version__ = '): 35 | __version__ = line.replace('__version__ = ','').split('\'')[1] 36 | break 37 | 38 | # Package name 39 | pname = 'ipa-deploy' 40 | 41 | # Generate links 42 | gurl = 'https://github.com/floatinghotpot/' + pname 43 | gdurl = gurl + '/tarball/' + __version__ 44 | 45 | setuptools.setup( 46 | name = pname, 47 | 48 | # Versions should comply with PEP440. For a discussion on single-sourcing 49 | # the version across setup.py and the project code, see 50 | # https://packaging.python.org/en/latest/single_source_version.html 51 | version = __version__, 52 | description = 'install IPA package to iOS device with command line', 53 | long_description = long_description, 54 | long_description_content_type='text/markdown', 55 | 56 | # The project's main homepage. 57 | url = 'https://github.com/floatinghotpot/ipa-deploy', 58 | download_url = 'https://github.com/floatinghotpot/ipa-deploy', 59 | 60 | # Author details 61 | author = 'Liming Xie', 62 | author_email = 'liming.xie@gmail.com', 63 | 64 | # Choose your license 65 | license = 'GPLv3+', 66 | 67 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 68 | classifiers=[ 69 | # How mature is this project? Common values are 70 | # 3 - Alpha 71 | # 4 - Beta 72 | # 5 - Production/Stable 73 | 'Development Status :: 4 - Beta', 74 | 75 | # Indicate who your project is intended for 76 | 'Intended Audience :: Developers', 77 | 78 | # Pick your license as you wish (should match "license" above) 79 | ('License :: OSI Approved :: ' + 80 | 'GNU General Public License v3 or later (GPLv3+)'), 81 | 82 | # Specify the Python versions you support here. In particular, ensure 83 | # that you indicate whether you support Python 2, Python 3 or both. 84 | 'Programming Language :: Python :: 3', 85 | 'Programming Language :: Python :: 3.7', 86 | 87 | # Operating Systems on which it runs 88 | 'Operating System :: OS Independent', 89 | ], 90 | 91 | # What does your project relate to? 92 | keywords=[ 93 | 'ios', 94 | 'installer', 95 | ], 96 | 97 | platforms = 'any', 98 | python_requires='>=3.7', 99 | 100 | # You can just specify the packages manually here if your project is 101 | # simple. Or you can use find_packages(). 102 | packages=setuptools.find_packages(exclude=['cache', 'output', 'log', 'samples', 'docs']), 103 | 104 | # List run-time dependencies here. 105 | # These will be installed by pip when your 106 | # project is installed. For an analysis of "install_requires" vs pip's 107 | # requirements files see: 108 | # https://packaging.python.org/en/latest/requirements.html 109 | install_requires = [ 110 | ], 111 | 112 | # List additional groups of dependencies here 113 | # (e.g. development dependencies). 114 | # You can install these using the following syntax, for example: 115 | # $ pip install -e .[dev,test] 116 | # extras_require={}, 117 | 118 | # If there are data files included in your packages that need to be 119 | # installed, specify them here. If using Python 2.6 or less, then these 120 | # have to be included in MANIFEST.in as well. 121 | # package_data={'sample': ['package_data.dat'],}, 122 | 123 | # Although 'package_data' is the preferred approach, in some case you may 124 | # need to place data files outside of your packages. See: 125 | # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files 126 | # In this case, 'data_file' will be installed into '/my_data' 127 | # data_files=[('my_data', ['data/data_file'])], 128 | 129 | # To provide executable scripts, use entry points in preference to the 130 | # "scripts" keyword. Entry points provide cross-platform support and allow 131 | # pip to create the appropriate form of executable for the target platform. 132 | # entry_points={'console_scripts': ['sample=sample:main',],}, 133 | entry_points={'console_scripts': ['ipa-deploy=ipa_deploy:cli_main']}, 134 | 135 | #scripts=['tools/hq.py'], 136 | ) 137 | --------------------------------------------------------------------------------