cocoapods-podfile-local 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +166 -0
- data/lib/cocoapods-podfile-local.rb +6 -0
- data/lib/cocoapods_plugin.rb +1 -0
- data/lib/cocoapods_podfile_local/dsl.rb +13 -0
- data/lib/cocoapods_podfile_local/hook.rb +57 -0
- data/lib/cocoapods_podfile_local/override_manager.rb +51 -0
- data/lib/cocoapods_podfile_local/version.rb +3 -0
- metadata +73 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8a2d1a17a228a1b6d6f1d73d24a9b85b66dd5492550d710a3bb373cea510ba3a
|
|
4
|
+
data.tar.gz: 39d97d0c86deb19c3eaf8f95bbc8f64a362b3b532949bb55c27eb6ba1b9ed964
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 96f8f2de20ce2f498e20a9d1468781baabbc9527e1cd037a5e4ed3f5c3a15dead86d9c9861db42e031901d9b8a181e40b808677f5bcd805e2bb8e9e805ff98b6
|
|
7
|
+
data.tar.gz: ac6653cab35a5938172d731abbb55c11e4e1a4f22d99f83743c3a01247b4ff8a85436b05fe926a8a92b7224af930dfb01e0ce6b688b2a2299fa15c42cc8f9fe1
|
data/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# cocoapods-podfile-local
|
|
2
|
+
|
|
3
|
+
一个 CocoaPods 插件,让每个开发者通过 `Podfile.local` 文件覆盖 pod 的引入方式(git 分支、本地路径等),无需修改共享的 Podfile。
|
|
4
|
+
|
|
5
|
+
## 使用方法
|
|
6
|
+
|
|
7
|
+
### 1. 创建 Podfile.local
|
|
8
|
+
|
|
9
|
+
在项目根目录创建 `Podfile.local`(已加入 `.gitignore`,不会被提交):
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
# 指向本地路径(联调开发最常用)
|
|
13
|
+
edit 'VKRouteKit', :path => '../VKRouteKit'
|
|
14
|
+
|
|
15
|
+
# 指向 git 仓库 + feature 分支
|
|
16
|
+
edit 'CustomLib', :git => 'git@gitlab.com:xxx/CustomLib.git', :branch => 'feature/login'
|
|
17
|
+
|
|
18
|
+
# 指向 git tag
|
|
19
|
+
edit 'SomeSDK', :git => 'git@gitlab.com:xxx/SomeSDK.git', :tag => 'v1.2.3'
|
|
20
|
+
|
|
21
|
+
# 指向特定 commit
|
|
22
|
+
edit 'SomeSDK', :git => 'git@gitlab.com:xxx/SomeSDK.git', :commit => 'abc123'
|
|
23
|
+
|
|
24
|
+
# 使用自定义 podspec
|
|
25
|
+
edit 'MyPod', :podspec => './local_specs/MyPod.podspec'
|
|
26
|
+
|
|
27
|
+
# 只覆盖非来源选项(保留原始版本/来源)
|
|
28
|
+
edit 'DebugTool', :configurations => ['Debug']
|
|
29
|
+
|
|
30
|
+
# 组合覆盖
|
|
31
|
+
edit 'VKWebBridge', :git => 'git@gitlab.com:xxx/VKWebBridge.git', :branch => 'hotfix/crash', :configurations => ['Debug']
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. 执行安装
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bundle exec pod install
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
控制台会输出覆盖日志:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
[Podfile.local] Loading /path/to/project/Podfile.local
|
|
44
|
+
[Podfile.local] Overriding 'VKRouteKit' with {:path=>"../VKRouteKit"}
|
|
45
|
+
[Podfile.local] Overriding 'CustomLib' with {:git=>"git@gitlab.com:xxx/CustomLib.git", :branch=>"feature/login"}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 3. 恢复原始状态
|
|
49
|
+
|
|
50
|
+
删除或清空 `Podfile.local`,重新 `pod install` 即可。
|
|
51
|
+
|
|
52
|
+
## 互斥源自动清理
|
|
53
|
+
|
|
54
|
+
覆盖来源时,插件会自动清除与之冲突的旧选项:
|
|
55
|
+
|
|
56
|
+
| edit 指定 | 自动清除 |
|
|
57
|
+
|---------------|---------------------------------------------|
|
|
58
|
+
| `:git` | `:path`, `:podspec` |
|
|
59
|
+
| `:path` | `:git`, `:branch`, `:tag`, `:commit`, `:podspec` |
|
|
60
|
+
| `:podspec` | `:git`, `:branch`, `:tag`, `:commit`, `:path` |
|
|
61
|
+
|
|
62
|
+
例如原始 pod 用 `:git` + `:branch` 引入,你 edit 为 `:path`,插件会自动清除 `:git` 和 `:branch`,无需手动处理。
|
|
63
|
+
|
|
64
|
+
## 工作原理
|
|
65
|
+
|
|
66
|
+
### CocoaPods 生命周期
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
pod install
|
|
70
|
+
│
|
|
71
|
+
▼
|
|
72
|
+
① 加载 gem(插件在此阶段加载) ← 插件读取 Podfile.local,拦截 pod 方法
|
|
73
|
+
│
|
|
74
|
+
▼
|
|
75
|
+
② 解析 Podfile(逐行执行 Ruby 代码) ← 拦截的 pod 方法在此阶段替换参数
|
|
76
|
+
│
|
|
77
|
+
▼
|
|
78
|
+
③ 解析依赖(resolver 计算版本来源) ← 此时 Kingfisher 已经是 :path 来源
|
|
79
|
+
│
|
|
80
|
+
▼
|
|
81
|
+
④ 下载 & 安装
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 核心机制
|
|
85
|
+
|
|
86
|
+
插件在加载阶段(早于 Podfile 解析)完成两件事:
|
|
87
|
+
|
|
88
|
+
**1. 读取 Podfile.local,收集覆盖配置**
|
|
89
|
+
|
|
90
|
+
创建一个独立的 `PodfileLocalLoader` 对象,用 `instance_eval` 执行 `Podfile.local` 内容。每个 `edit` 调用将覆盖选项注册到 `OverrideManager` 单例。
|
|
91
|
+
|
|
92
|
+
**2. 用 `prepend` 拦截 `Pod::Podfile::DSL#pod` 方法**
|
|
93
|
+
|
|
94
|
+
在 `pod` 方法的查找链前面插入一个包装模块。当 Podfile 解析到 `pod 'Kingfisher', '~> 8.0'` 时:
|
|
95
|
+
|
|
96
|
+
1. 包装方法检查 OverrideManager 中是否有 `'Kingfisher'` 的覆盖 → 有
|
|
97
|
+
2. 从原始参数中分离版本号(`'~> 8.0'`)和选项 Hash
|
|
98
|
+
3. 执行互斥源清理 + 选项合并
|
|
99
|
+
4. 如果覆盖指定了 `:path` / `:git` / `:podspec`,移除版本号约束
|
|
100
|
+
5. 用合并后的参数调用 `super`(原始 `pod` 方法)
|
|
101
|
+
|
|
102
|
+
对 CocoaPods 来说,就好像 Podfile 里直接写的就是覆盖后的内容。
|
|
103
|
+
|
|
104
|
+
### 时序图
|
|
105
|
+
|
|
106
|
+
```mermaid
|
|
107
|
+
sequenceDiagram
|
|
108
|
+
participant User as pod install
|
|
109
|
+
participant CP as CocoaPods
|
|
110
|
+
participant Plugin as cocoapods-podfile-local
|
|
111
|
+
participant OM as OverrideManager
|
|
112
|
+
participant PF as Podfile
|
|
113
|
+
|
|
114
|
+
User->>CP: bundle exec pod install
|
|
115
|
+
CP->>Plugin: require cocoapods_plugin.rb
|
|
116
|
+
Plugin->>Plugin: setup!
|
|
117
|
+
Plugin->>Plugin: load_podfile_local!
|
|
118
|
+
Plugin->>OM: edit 'Kingfisher', path:... → register
|
|
119
|
+
Plugin->>Plugin: patch_pod_dsl! (prepend)
|
|
120
|
+
|
|
121
|
+
CP->>PF: 开始解析 Podfile
|
|
122
|
+
PF->>Plugin: pod 'Alamofire', '~> 5.9'
|
|
123
|
+
Plugin->>OM: 有覆盖? → 无
|
|
124
|
+
Plugin->>CP: super → 原样注册
|
|
125
|
+
|
|
126
|
+
PF->>Plugin: pod 'Kingfisher', '~> 8.0'
|
|
127
|
+
Plugin->>OM: 有覆盖? → 有!
|
|
128
|
+
OM->>OM: merge + 互斥源清理
|
|
129
|
+
Plugin->>CP: super('Kingfisher', path:'...') → 替换注册
|
|
130
|
+
|
|
131
|
+
CP->>CP: 解析依赖 (Kingfisher 已是 path 来源)
|
|
132
|
+
CP->>CP: 下载 & 安装
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 文件结构
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
plugins/cocoapods-podfile-local/
|
|
139
|
+
├── cocoapods-podfile-local.gemspec # gem 描述
|
|
140
|
+
├── README.md # 本文档
|
|
141
|
+
└── lib/
|
|
142
|
+
├── cocoapods_plugin.rb # CocoaPods 插件入口(硬性约定)
|
|
143
|
+
├── cocoapods-podfile-local.rb # 主入口,require 各模块 + 调用 setup!
|
|
144
|
+
└── cocoapods_podfile_local/
|
|
145
|
+
├── version.rb # 版本号
|
|
146
|
+
├── dsl.rb # 注入 edit 方法到 Pod::Podfile
|
|
147
|
+
├── override_manager.rb # 覆盖注册、互斥清理、选项合并
|
|
148
|
+
└── hook.rb # 加载 Podfile.local + prepend 拦截 pod 方法
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 关键设计决策
|
|
152
|
+
|
|
153
|
+
- **为什么不用 `pre_install` hook?** `pre_install` 在依赖解析之后才执行,此时修改 Dependency 对象已经不影响解析结果。必须在 Podfile 解析阶段拦截。
|
|
154
|
+
|
|
155
|
+
- **为什么用 `prepend` 而不是 `alias_method`?** `prepend` 是 Ruby 推荐的方法拦截方式,在方法查找链中插入模块,可以用 `super` 调用原始方法,不会污染命名空间。
|
|
156
|
+
|
|
157
|
+
- **为什么 Podfile.local 用独立 Loader 而不是在 Podfile 上下文中 eval?** 插件加载时 Podfile 实例尚不存在,无法在其上下文中执行。用独立的 `PodfileLocalLoader` 对象来收集配置,与 Podfile 解析过程解耦。
|
|
158
|
+
|
|
159
|
+
- **为什么 `cocoapods_plugin.rb` 是必须的?** CocoaPods 通过检查 gem 中是否存在 `lib/cocoapods_plugin.rb` 来判断是否为合法插件,这是硬性约定。
|
|
160
|
+
|
|
161
|
+
## 注意事项
|
|
162
|
+
|
|
163
|
+
- `Podfile.local` 已加入 `.gitignore`,每个开发者独立维护
|
|
164
|
+
- edit 一个 Podfile 中不存在的 pod 会输出警告但不会中断安装
|
|
165
|
+
- 首次使用需要先 `bundle install` 安装插件
|
|
166
|
+
- 插件作为本地 gem 内嵌在项目中,无需发布到 RubyGems
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'cocoapods-podfile-local'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module CocoapodsPodfileLocal
|
|
2
|
+
module DSL
|
|
3
|
+
def edit(pod_name, options = {})
|
|
4
|
+
CocoapodsPodfileLocal::OverrideManager.instance.register(pod_name, options)
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module Pod
|
|
10
|
+
class Podfile
|
|
11
|
+
include CocoapodsPodfileLocal::DSL
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module CocoapodsPodfileLocal
|
|
2
|
+
TAG = '[Podfile.local]'.freeze
|
|
3
|
+
|
|
4
|
+
class PodfileLocalLoader
|
|
5
|
+
def edit(pod_name, options = {})
|
|
6
|
+
OverrideManager.instance.register(pod_name, options)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def setup!
|
|
12
|
+
load_podfile_local!
|
|
13
|
+
return if OverrideManager.instance.empty?
|
|
14
|
+
patch_pod_dsl!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def load_podfile_local!
|
|
20
|
+
local_file = Pathname.pwd + 'Podfile.local'
|
|
21
|
+
return unless local_file.exist?
|
|
22
|
+
|
|
23
|
+
Pod::UI.message "#{TAG} Loading #{local_file}"
|
|
24
|
+
loader = PodfileLocalLoader.new
|
|
25
|
+
loader.instance_eval(local_file.read, local_file.to_s)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def patch_pod_dsl!
|
|
29
|
+
manager = OverrideManager.instance
|
|
30
|
+
|
|
31
|
+
Pod::Podfile::DSL.prepend(Module.new do
|
|
32
|
+
define_method(:pod) do |name = nil, *requirements|
|
|
33
|
+
if name && manager.overrides.key?(name)
|
|
34
|
+
override_opts = manager.overrides[name]
|
|
35
|
+
|
|
36
|
+
original_opts = requirements.last.is_a?(Hash) ? requirements.pop : {}
|
|
37
|
+
version_reqs = requirements
|
|
38
|
+
|
|
39
|
+
merged = manager.merge(original_opts, override_opts)
|
|
40
|
+
|
|
41
|
+
if merged.key?(:path) || merged.key?(:git) || merged.key?(:podspec)
|
|
42
|
+
version_reqs = []
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Pod::UI.message "#{TAG} Overriding '#{name}' with #{merged}"
|
|
46
|
+
|
|
47
|
+
new_requirements = version_reqs
|
|
48
|
+
new_requirements << merged unless merged.empty?
|
|
49
|
+
super(name, *new_requirements)
|
|
50
|
+
else
|
|
51
|
+
super(name, *requirements)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module CocoapodsPodfileLocal
|
|
2
|
+
class OverrideManager
|
|
3
|
+
SOURCE_KEYS = %i[git path podspec].freeze
|
|
4
|
+
GIT_EXTRAS = %i[branch tag commit].freeze
|
|
5
|
+
|
|
6
|
+
EXCLUSIVE_RULES = {
|
|
7
|
+
git: %i[path podspec],
|
|
8
|
+
path: %i[git branch tag commit podspec],
|
|
9
|
+
podspec: %i[git branch tag commit path],
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
def self.instance
|
|
13
|
+
@instance ||= new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.reset!
|
|
17
|
+
@instance = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@overrides = {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def register(pod_name, options)
|
|
25
|
+
@overrides[pod_name] = options
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def overrides
|
|
29
|
+
@overrides.dup
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def empty?
|
|
33
|
+
@overrides.empty?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Merge override options into the original options, with exclusive-source cleanup.
|
|
37
|
+
def merge(original_options, override_options)
|
|
38
|
+
merged = original_options.dup
|
|
39
|
+
|
|
40
|
+
keys_to_remove = []
|
|
41
|
+
EXCLUSIVE_RULES.each do |source_key, conflicting_keys|
|
|
42
|
+
if override_options.key?(source_key)
|
|
43
|
+
keys_to_remove.concat(conflicting_keys)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
keys_to_remove.uniq.each { |k| merged.delete(k) }
|
|
48
|
+
merged.merge(override_options)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cocoapods-podfile-local
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- dairuiquan
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-04-15 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: cocoapods
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.10'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '2.0'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '1.10'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.0'
|
|
33
|
+
description: |
|
|
34
|
+
A CocoaPods plugin that lets each developer maintain a local Podfile.local
|
|
35
|
+
file (git-ignored) to override pod sources — pointing to git branches,
|
|
36
|
+
local paths, tags, commits, or custom podspecs — without modifying the
|
|
37
|
+
shared Podfile.
|
|
38
|
+
email:
|
|
39
|
+
executables: []
|
|
40
|
+
extensions: []
|
|
41
|
+
extra_rdoc_files: []
|
|
42
|
+
files:
|
|
43
|
+
- README.md
|
|
44
|
+
- lib/cocoapods-podfile-local.rb
|
|
45
|
+
- lib/cocoapods_plugin.rb
|
|
46
|
+
- lib/cocoapods_podfile_local/dsl.rb
|
|
47
|
+
- lib/cocoapods_podfile_local/hook.rb
|
|
48
|
+
- lib/cocoapods_podfile_local/override_manager.rb
|
|
49
|
+
- lib/cocoapods_podfile_local/version.rb
|
|
50
|
+
homepage: https://rubygems.org/gems/cocoapods-podfile-local
|
|
51
|
+
licenses:
|
|
52
|
+
- MIT
|
|
53
|
+
metadata: {}
|
|
54
|
+
post_install_message:
|
|
55
|
+
rdoc_options: []
|
|
56
|
+
require_paths:
|
|
57
|
+
- lib
|
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '2.6'
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
requirements: []
|
|
69
|
+
rubygems_version: 3.4.1
|
|
70
|
+
signing_key:
|
|
71
|
+
specification_version: 4
|
|
72
|
+
summary: Override pod sources via Podfile.local without touching the shared Podfile.
|
|
73
|
+
test_files: []
|