├── .gitignore ├── README.md ├── package.json └── patch.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 💊 pod-patch [![npm version](https://badge.fury.io/js/pod-patch.svg)](https://badge.fury.io/js/pod-patch) 2 | 3 | Patching the Pods `podspec` files in **React Native** projects with the version tracking and `Podfile` updates. 4 | 5 | # Why 6 | 7 | When developing something using Cocoapods packages in some cases you need to modify the Pod's `podspec` file. Often these cases are: 8 | 9 | - Change the Pod dependency, 10 | - Modify compilation flags, paths, parameters, 11 | - Using the Pod with connected sources or libraries from another Pod. 12 | 13 | You can do it by hand, download `podspec` file, modify, it and point to the local `podspec` file at the main **Podfile**. But what if I say: 14 | 15 | - That you need to patch a few Pods? 16 | - Their versions are changing too? 17 | - What if there are a bunch of the patched Pods with the different versions? 18 | 19 | How not forget what and where was patched and patch them on the new versions or Podfile changes? 20 | 21 | **🎈 This small tool was created to solve this!** 22 | 23 | # Configuration 24 | 25 | ### This tool is created for use in the **React Native** project. 26 | 27 | As this tool doesn't require many parameters we are using the **convention over configuration** approach. 28 | 29 | By default tool will look into the `native/ios/pod-patch` directory for the `.patch` files. The file name itself tells the tool which Pod and which version you want to patch the Pod's `podspec` and use it in your main Podfile. 30 | 31 | The naming convention for the `.patch` files is `podName@version.patch` where `podName` is the name of the Pod and `version` is the Pod version to use for the patch apply. 32 | 33 | For example, `native/ios/pod-patch/gRPC-Core@1.40.0.patch` will tell that we want to apply patch from this file to the `gRPC-Core` podspec file for the `1.40.0` version. 34 | 35 | Also, you can use it without a version. When using `native/ios/pod-patch/gRPC-Core.patch` tool will apply the patch from this file to the `gRPC-Core` pod with the version from your Podfile. When using without a version you need to have a record in the Podfile with the pod and version. 36 | 37 | For example: 38 | 39 | ```ruby 40 | target 'App' do 41 | ... 42 | pod 'gRPC-Core', '1.40.0' 43 | ``` 44 | 45 | You can have as many `.patch` files as you need, the tool will use all of them. 46 | 47 | # Running 48 | 49 | The tool can be executed as the `npx pod-patch` command in the `native` directory of your **React Native** project. 50 | 51 | When running the tool will iterate through your `.patch` files checks if anything has changed and made some magic: 52 | 53 | - Checks if there is no version conflicts in your `Podfile` and `.patch` file, 54 | - Download a `podspec` file for your Pod from the [cocoapods git repo](https://github.com/CocoaPods/Specs/tree/master/Specs) to the `native/ios/pod-patch/.patched/{pod-name}/{pod-version}/` directory, 55 | - Apply the patch from the `.patch` file to it, 56 | - Changes the record for the patched **Pod** in the `Podfile` to point it to the local patched podspec. For example, the record for the `gRPC-Core` will automatically change to: 57 | 58 | ```ruby 59 | target 'App' do 60 | ... 61 | pod 'gRPC-Core', :podspec => './pod-patch/.patched/gRPC-Core/1.40.0/gRPC-Core.podspec.json' 62 | ``` 63 | 64 | The tool checks if the Pod is already patched. 65 | If nothing changed from the already applied patches - it will do nothing. 66 | 67 | [![asciicast](https://asciinema.org/a/t77IAxPy1ZEZ84MAesfE4K2Bv.svg)](https://asciinema.org/a/t77IAxPy1ZEZ84MAesfE4K2Bv) 68 | 69 | ## Using with the `yarn` or `npm i` 70 | 71 | A good practice is to use it linked with the running of `yarn` or `npm i` in the `native` directory in your install script in the `package.json` before the `pod install` execution. 72 | 73 | This will updates/install the packages with the transparent checking if all of the Pod patches are up-to-date or need to be applied if something in the `.patch` file changed or `Podspec` has new changes in the pod dependency or version changes before the `pod install`. 74 | 75 | If using this way with the `git` repo you can add `native/ios/pod-patch/.patched` directory to your `.gitignore`. Because when the tool runs it will check the existence of the local patched podspec files and create those that not exists. 76 | 77 | # Pod version changing 78 | 79 | In case when the Pod version changed but you already have a `.patch` file for the previous version and it is already applied, but you want to upgrade the Pod and patch to the new version there are three simple steps: 80 | 81 | **First**, if your `.patch` file in the `native/ios/pod-patch` has a version format i.e. `gRPC-Core@1.40.0.patch` you need to create a patch file for the new version i.e. `gRPC-Core@1.41.0.patch`. 82 | 83 | If the `.patch` file in the no-version format i.e. `gRPC-Core.patch` you do nothing here as this is an universal patch for all versions. 84 | 85 | **Second**, you need to point to the new version of the Pod in your `Podfile`. For example, upgrading to 1.41.0, need to look like: 86 | 87 | ```ruby 88 | target 'App' do 89 | ... 90 | pod 'gRPC-Core', '1.41.0' 91 | ``` 92 | 93 | **Third**, you need to run `npx pod-patch` from your `native` directory and the tool will create a new patched Pod and point Podfile to it 🙌. 94 | 95 | If you have a version-agnostic `.patch` file, actually you only need to do a second step only (point to the new version at the Podfile) and just run the tool! 96 | 97 | # Command line flags 98 | 99 | - `-h`: Output the command usage help. 100 | - `-v`: Output the script version. 101 | - `-p`: Path to the directory where the `.patch` files are if it differs from the default `native/ios/pod-patch`. 102 | - `-d`: Path to the `Podfile` if it differs from the default `native/ios/Podfile`. 103 | 104 | # Todo 105 | 106 | - [ ] Resolving conflicts if there are a few patch files for one Pod present. 107 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pod-patch", 3 | "version": "0.0.11", 4 | "description": "Patching the Pod specfiles in ReactNative projects.", 5 | "main": "index.js", 6 | "bin":{ 7 | "pod-patch": "patch.sh" 8 | }, 9 | "scripts": { 10 | "patch": "node_modules/pod-patch/patch.sh", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/geekbrother/pod-patch.git" 16 | }, 17 | "keywords": [ 18 | "Pod", 19 | "Podspec", 20 | "patch", 21 | "patching" 22 | ], 23 | "author":{ 24 | "name" : "Max Kalashnikoff", 25 | "email" : "geekmaks@gmail.com", 26 | "url" : "https://github.com/geekbrother" 27 | }, 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/geekbrother/pod-patch/issues" 31 | }, 32 | "homepage": "https://github.com/geekbrother/pod-patch#readme" 33 | } 34 | -------------------------------------------------------------------------------- /patch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Description: 3 | # Podspec auto patching script for React Native projects. 4 | # It download, patches the podspec and updating Podfile 5 | # based on the PodName@Version.patch files convention. 6 | # 7 | # Author: Max Kalashnikoff (geekmaks@gmail.com, max@comm.app) 8 | # Created for: https://comm.app 9 | # 10 | # Usage: npx pod-patch -h 11 | readonly SCRIPT_VERSION='0.0.11' 12 | set -e 13 | 14 | # Default parameters 15 | ## Directory relatively to the ReactNative '/native' directory where the .patch files are. 16 | PATCHES_DIR="./ios/pod-patch" 17 | ## Cocoapods Podfile path 18 | PODFILE_PATH="./ios/Podfile" 19 | ## Where the patched files will be placed 20 | readonly PATCHED_SUBDIR=".patched" 21 | ## Podspecs GitHub url for Cocoapods repository 22 | readonly PODSPECS_URL="https://raw.githubusercontent.com/CocoaPods/Specs" 23 | 24 | # Parsing CLI arguments 25 | while getopts ":hvp:d:" opt; do 26 | case ${opt} in 27 | h) 28 | echo "Version: ${SCRIPT_VERSION}" 29 | echo "Usage: npx pod-patch [-h Usage] [-v Version] [-d Podfile path ] [-p .patch files directory]" 30 | echo "Description:" 31 | echo "pod-patch is a Podspec files patching tool based on the .patch files." 32 | echo "Run it as npx or bash script from the 'native' directory of the ReactNative project." 33 | exit 0 34 | ;; 35 | v) 36 | echo "pod-patch version: ${SCRIPT_VERSION}" 37 | exit 0 38 | ;; 39 | p) 40 | PATCHES_DIR=${OPTARG} 41 | ;; 42 | d) 43 | PODFILE_PATH=${OPTARG} 44 | ;; 45 | esac 46 | done 47 | shift $((OPTIND - 1)) 48 | 49 | # Console logging 50 | # Usage: LOG SKIP|INFO|SUCCESS|ERROR "Message text" 51 | function LOG() { 52 | local CRED=$(tput setaf 1) 53 | local CGREEN=$(tput setaf 2) 54 | local CYELLOW=$(tput setaf 3) 55 | local CRESET=$(tput sgr0) 56 | 57 | case $1 in 58 | SKIP) echo "[skip] $2" ;; 59 | INFO) echo "${CYELLOW}[info] $2${CRESET}" ;; 60 | SUCCESS) echo "${CGREEN}[success] $2${CRESET}" ;; 61 | ERROR) echo "${CRED}[error] $2${CRESET}" && exit 1 ;; 62 | *) echo "[log] $2" ;; 63 | esac 64 | } 65 | 66 | # Patching function 67 | # Usage: MAKE_PATCH $POD_NAME $POD_VERSION 68 | function MAKE_PATCH() { 69 | # Assigning arguments 70 | local readonly POD_NAME=$1 71 | local POD_VERSION=$2 72 | 73 | if [ -z "$POD_VERSION" ]; then 74 | local PATCH_FILE="${PATCHES_DIR}/${POD_NAME}.patch" 75 | else 76 | local PATCH_FILE="${PATCHES_DIR}/${POD_NAME}@${POD_VERSION}.patch" 77 | fi 78 | 79 | # Read the version of the Pod from the Podfile 80 | local POD_VERSION_PODFILE=$(cat ${PODFILE_PATH} | grep "pod '${POD_NAME}'" | awk '{print $3}' | tr -d "'") 81 | if [[ $POD_VERSION_PODFILE =~ ^[0-9]+(\.[0-9]+){2,3}$ ]]; then 82 | LOG INFO "${POD_NAME} has ${POD_VERSION_PODFILE} version in the Podfile" 83 | # If POD_VERSION is empty we have a .patch file without version, go with the 84 | # version from the Podfile 85 | if [ -z "$POD_VERSION" ]; then 86 | POD_VERSION=$POD_VERSION_PODFILE 87 | fi 88 | # Error if Podfile version not equal with the .patch version 89 | if [ $POD_VERSION_PODFILE != $POD_VERSION ]; then 90 | LOG ERROR "Podfile version ${POD_VERSION_PODFILE} not equal to patch version ${POD_VERSION}" 91 | fi 92 | elif [[ $POD_VERSION_PODFILE =~ .*podspec.* ]]; then 93 | # If already patched and have a ':podspec =>' we need to check if the patched podspec file exists 94 | # in case of this is a git copy with the .patched directory in .gitignore 95 | local PODSPEC_PODFILE=$(cat ${PODFILE_PATH} | grep "pod '${POD_NAME}'" | awk '{print $5}' | tr -d "'") 96 | if [[ -f "./ios/${PODSPEC_PODFILE##./}" ]]; then 97 | LOG SKIP "${POD_NAME} podspec already patched and has a local podspec file in a Podfile" 98 | return 99 | else 100 | LOG INFO "Patched podspec file not found, creating a new one" 101 | fi 102 | else 103 | LOG ERROR "Wrong ${POD_NAME} pod version ${POD_VERSION_PODFILE} in the Podfile" 104 | fi 105 | 106 | # Getting Cocoapods github Spec url from the local path of the Pod 107 | SPEC_PATH=$(pod spec which ${POD_NAME}) 108 | SPEC_PATH=${SPEC_PATH##*/trunk} 109 | SPEC_PATH=${SPEC_PATH%/*} 110 | SPEC_PATH=${SPEC_PATH%/*} 111 | local GITHUB_SPEC_URL="${PODSPECS_URL}/master${SPEC_PATH}/${POD_VERSION}/${POD_NAME}.podspec.json" 112 | 113 | local PATCHED_PODSPEC_PATCH="${PATCHES_DIR}/${PATCHED_SUBDIR}/${POD_NAME}/${POD_VERSION}/${POD_NAME}.podspec.json" 114 | rm -f "${PATCHED_PODSPEC_PATCH}" 115 | mkdir -p "${PATCHES_DIR}/${PATCHED_SUBDIR}/${POD_NAME}/${POD_VERSION}" 116 | 117 | # Download the podspec file for the pod 118 | local CODE=$(curl -sSL -w '%{http_code}' --output "${PATCHED_PODSPEC_PATCH}" "${GITHUB_SPEC_URL}") 119 | if [[ "$CODE" =~ ^2 ]]; then 120 | LOG INFO "Podspec downloaded" 121 | elif [[ "$CODE" == 404 ]]; then 122 | LOG ERROR "Got 404 error from: ${GITHUB_SPEC_URL}" 123 | else 124 | LOG ERROR "ERROR: server returned HTTP code $CODE when downloading from ${GITHUB_SPEC_URL}" 125 | fi 126 | 127 | # Patch the downloaded podspec file and replace the Pod 128 | # in Podfile with the local podspec path 129 | patch "${PATCHED_PODSPEC_PATCH}" "${PATCH_FILE}" 130 | sed -i.bak -e "s|.*pod.*'${POD_NAME}'.*| pod '${POD_NAME}', :podspec => './${PATCHED_PODSPEC_PATCH#"./ios/"}'|" ${PODFILE_PATH} 131 | rm -f "${PODFILE_PATH}.bak" 132 | 133 | LOG SUCCESS "Podfile updated with the ${POD_NAME} patched pod" 134 | } 135 | 136 | # Check if the directory with the .patch files exists 137 | if [[ ! -d $PATCHES_DIR ]]; then 138 | LOG ERROR "Directory with .patch files doesn't exist: ${PATCHES_DIR}" 139 | fi 140 | # Check if the Podfile exists 141 | if [[ ! -f $PODFILE_PATH ]]; then 142 | LOG ERROR "Podfile not found: ${PODFILE_PATH}" 143 | fi 144 | 145 | # Read the directory with the .patch files 146 | # and extract the Pod name and version from 147 | # the .patch filename 148 | # We can have a two types of the .patch files: 149 | # - podName@Version.patch: With the patch for the specific version of the Pod. 150 | # - podName.patch: Without specific version. It will be applied to all versions. 151 | for TO_PATCH_FILE in "${PATCHES_DIR}"/*.patch; do 152 | if [ -f "$TO_PATCH_FILE" ]; then 153 | TO_PATCH_FILE=$(basename $TO_PATCH_FILE | awk '{print $1}') 154 | # If the .patch file with the specific version: 155 | if [[ $TO_PATCH_FILE =~ "@" ]]; then 156 | POD_NAME="${TO_PATCH_FILE%%@*}" 157 | POD_VERSION=$(echo $TO_PATCH_FILE | awk -F "@" '{print $2}' | awk -F ".patch" '{print $1}') 158 | LOG INFO "Found ${POD_NAME} pod patch for @${POD_VERSION} version" 159 | else 160 | # If the .patch file without version 161 | POD_NAME="${TO_PATCH_FILE%".patch"}" 162 | POD_VERSION="" 163 | LOG INFO "Found ${POD_NAME} pod patch for all versions" 164 | fi 165 | # Patching Pod with the certain version 166 | MAKE_PATCH $POD_NAME $POD_VERSION 167 | fi 168 | done 169 | --------------------------------------------------------------------------------