app_store_dev_api 0.3.0 → 0.3.1
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 +4 -4
- data/.DS_Store +0 -0
- data/.claude/settings.local.json +2 -19
- data/Gemfile +5 -0
- data/Gemfile.lock +6 -0
- data/docs/bundle_id_capabilities.md +382 -0
- data/docs/bundle_ids.md +417 -0
- data/docs/creating_api_keys.md +137 -0
- data/docs/generating_tokens.md +189 -0
- data/docs/openapi.oas4.3.json +230597 -0
- data/docs/pindo_usage_reference.md +234 -0
- data/docs/revoking_api_keys.md +118 -0
- data/install_local.sh +73 -62
- data/lib/.DS_Store +0 -0
- data/lib/app_store_dev_api/client/builder.rb +1 -1
- data/lib/app_store_dev_api/client/options.rb +1 -1
- data/lib/app_store_dev_api/client.rb +1 -1
- data/lib/app_store_dev_api/version.rb +1 -1
- data/lib/config/{schema_v4.2.json → schema_v4.3.json} +0 -10
- data/push_all.sh +6 -0
- data/release_remote.sh +27 -8
- data/scripts/comprehensive_validation.rb +1 -1
- data/scripts/final_validation_report.rb +1 -1
- data/test_library.rb +160 -0
- metadata +13 -4
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Pindo 项目中 app_store_dev_api 使用参考
|
|
2
|
+
|
|
3
|
+
> 本文档记录 [pindo](../../pindo) CLI 工具如何使用 `app_store_dev_api` gem 进行 App Store Connect API 认证和调用,作为集成参考。
|
|
4
|
+
|
|
5
|
+
## Gem 依赖
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
# pindo.gemspec
|
|
9
|
+
spec.add_runtime_dependency "app_store_dev_api", '~> 0.3.0'
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 认证流程
|
|
13
|
+
|
|
14
|
+
Pindo 采用 **本地 JSON 文件 + 交互式输入** 的方式管理 API 密钥凭证。
|
|
15
|
+
|
|
16
|
+
### 流程图
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
启动命令 (pindo appstore iap)
|
|
20
|
+
↓
|
|
21
|
+
从项目 config.json 读取 apple_id、bundle_id
|
|
22
|
+
↓
|
|
23
|
+
检查 {pindo_dir}/api_key.json 是否存在该 apple_id 的凭证
|
|
24
|
+
↓
|
|
25
|
+
┌─── 存在 ───┐ ┌─── 不存在 ───┐
|
|
26
|
+
│ │ │ │
|
|
27
|
+
│ 直接加载 │ │ 交互式输入 │
|
|
28
|
+
│ issuer_id │ │ issuer_id │
|
|
29
|
+
│ key_id │ │ key_id │
|
|
30
|
+
│ private_key│ │ .p8 文件路径 │
|
|
31
|
+
│ │ │ │
|
|
32
|
+
└─────┬──────┘ │ 读取 .p8 文件 │
|
|
33
|
+
│ │ 用户确认 │
|
|
34
|
+
│ │ 保存到 JSON │
|
|
35
|
+
│ └──────┬────────┘
|
|
36
|
+
│ │
|
|
37
|
+
└────────┬───────────────┘
|
|
38
|
+
↓
|
|
39
|
+
AppStoreDevApi::Client.new(
|
|
40
|
+
issuer_id: issuer_id,
|
|
41
|
+
key_id: key_id,
|
|
42
|
+
private_key: private_key
|
|
43
|
+
)
|
|
44
|
+
↓
|
|
45
|
+
开始调用 API
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 核心代码
|
|
49
|
+
|
|
50
|
+
**文件:** `pindo/lib/pindo/command/appstore/iap.rb`
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
def login_api_key(apple_id: nil)
|
|
54
|
+
load_apikey_config = false
|
|
55
|
+
api_key_json = nil
|
|
56
|
+
pindo_dir = File.expand_path(pindo_single_config.pindo_dir)
|
|
57
|
+
api_key_file = File.join(pindo_dir, "api_key.json")
|
|
58
|
+
|
|
59
|
+
# 步骤1:尝试从本地配置文件加载
|
|
60
|
+
if File.exist?(api_key_file)
|
|
61
|
+
api_key_json = JSON.parse(File.read(api_key_file))
|
|
62
|
+
if !api_key_json.nil? && !api_key_json[apple_id].nil? && !api_key_json[apple_id]["issuer_id"].nil?
|
|
63
|
+
issuer_id = api_key_json[apple_id]["issuer_id"]
|
|
64
|
+
key_id = api_key_json[apple_id]["key_id"]
|
|
65
|
+
private_key = api_key_json[apple_id]["private_key"]
|
|
66
|
+
load_apikey_config = true
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# 步骤2:如果未加载,交互式输入凭证
|
|
71
|
+
if !load_apikey_config
|
|
72
|
+
issuer_id = ask('请输入 issuer_id: ')
|
|
73
|
+
key_id = ask('请输入 key_id: ')
|
|
74
|
+
private_key_file = ask('请输入private key的文件路径: ')
|
|
75
|
+
private_key = File.read(File.new(private_key_file))
|
|
76
|
+
|
|
77
|
+
# 构建配置
|
|
78
|
+
api_key_json = api_key_json || {}
|
|
79
|
+
api_key_json[apple_id] = api_key_json[apple_id] || {}
|
|
80
|
+
api_key_json[apple_id]["issuer_id"] = issuer_id
|
|
81
|
+
api_key_json[apple_id]["key_id"] = key_id
|
|
82
|
+
api_key_json[apple_id]["private_key"] = private_key
|
|
83
|
+
|
|
84
|
+
# 步骤3:用户确认
|
|
85
|
+
answer = agree("请确认上面信息是否正确(Y/n):")
|
|
86
|
+
unless answer
|
|
87
|
+
raise Informative, "重新输入 !!!"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# 步骤4:持久化到本地文件
|
|
91
|
+
File.open(api_key_file, "w") do |f|
|
|
92
|
+
f.write(JSON.pretty_generate(api_key_json))
|
|
93
|
+
end
|
|
94
|
+
load_apikey_config = true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# 步骤5:初始化 API 客户端
|
|
98
|
+
if load_apikey_config
|
|
99
|
+
@app_store_connect = AppStoreDevApi::Client.new(
|
|
100
|
+
issuer_id: issuer_id,
|
|
101
|
+
key_id: key_id,
|
|
102
|
+
private_key: private_key
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 凭证存储格式
|
|
109
|
+
|
|
110
|
+
### 存储路径
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
{pindo_dir}/api_key.json
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`pindo_dir` 由 `pindo_single_config.pindo_dir` 确定,通常位于用户本地配置目录。
|
|
117
|
+
|
|
118
|
+
### JSON 结构(支持多账户)
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"user1@example.com": {
|
|
123
|
+
"issuer_id": "57246542-96fe-1a63-e053-0824d011072a",
|
|
124
|
+
"key_id": "2X9R4HXF34",
|
|
125
|
+
"private_key": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg...\n-----END PRIVATE KEY-----"
|
|
126
|
+
},
|
|
127
|
+
"user2@example.com": {
|
|
128
|
+
"issuer_id": "69384721-abcd-efgh-ijkl-123456789012",
|
|
129
|
+
"key_id": "ABC123DEF4",
|
|
130
|
+
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**关键设计:**
|
|
136
|
+
- 以 Apple ID(邮箱)作为一级 key,支持多账户切换
|
|
137
|
+
- `private_key` 直接存储 PEM 格式的私钥内容(非文件路径)
|
|
138
|
+
- 首次输入时从 `.p8` 文件读取,之后直接使用存储的内容
|
|
139
|
+
|
|
140
|
+
## API 调用示例
|
|
141
|
+
|
|
142
|
+
### 初始化入口
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
def run
|
|
146
|
+
# 从项目 config.json 加载 apple_id 和 bundle_id
|
|
147
|
+
config_file = File.join(project_dir, "config.json")
|
|
148
|
+
config_parser = Pindo::IosConfigParser.instance
|
|
149
|
+
config_parser.load_config(config_file: config_file)
|
|
150
|
+
@bundle_id = config_parser.bundle_id
|
|
151
|
+
@apple_id = config_parser.apple_id
|
|
152
|
+
|
|
153
|
+
# 登录并初始化客户端
|
|
154
|
+
login_api_key(apple_id: @apple_id)
|
|
155
|
+
|
|
156
|
+
# 开始使用 API...
|
|
157
|
+
end
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 查询应用
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
app_list_response = @app_store_connect.list_apps(
|
|
164
|
+
filter: { bundle_id: @bundle_id }
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 获取内购项目
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
# 消耗型内购
|
|
172
|
+
get_consumable_iap_items(
|
|
173
|
+
appstore_client: @app_store_connect,
|
|
174
|
+
app_id: app_id,
|
|
175
|
+
in_app_purchase_type: "CONSUMABLE"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# 订阅组
|
|
179
|
+
get_subscriptions_groups(
|
|
180
|
+
appstore_client: @app_store_connect,
|
|
181
|
+
app_id: app_id
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 沙盒测试账户
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
@app_store_connect.list_sandbox_testers()
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## 其他集成点
|
|
192
|
+
|
|
193
|
+
Pindo 中还有以下任务类支持 API Key 认证参数(通过 options 传递):
|
|
194
|
+
|
|
195
|
+
### 截图上传任务
|
|
196
|
+
|
|
197
|
+
**文件:** `pindo/lib/pindo/module/task/model/appstore/appstore_upload_screenshot_task.rb`
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
def initialize(app_id, screenshot_path, options = {})
|
|
201
|
+
@api_key_id = options[:api_key_id] # API Key ID
|
|
202
|
+
@api_issuer_id = options[:api_issuer_id] # Issuer ID
|
|
203
|
+
@api_key_path = options[:api_key_path] # .p8 文件路径
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 元数据上传任务
|
|
208
|
+
|
|
209
|
+
**文件:** `pindo/lib/pindo/module/task/model/appstore/appstore_upload_metadata_task.rb`
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
@api_key_id = options[:api_key_id]
|
|
213
|
+
@api_issuer_id = options[:api_issuer_id]
|
|
214
|
+
@api_key_path = options[:api_key_path]
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
> 注意:这些任务类使用的是 **文件路径** (`api_key_path`) 而非私钥内容,与 IAP 命令的处理方式略有不同。
|
|
218
|
+
|
|
219
|
+
## 设计要点总结
|
|
220
|
+
|
|
221
|
+
| 特性 | 说明 |
|
|
222
|
+
|------|------|
|
|
223
|
+
| 多账户支持 | 以 Apple ID 为 key 管理多套凭证 |
|
|
224
|
+
| 本地持久化 | JSON 文件存储,首次输入后免重复 |
|
|
225
|
+
| 交互式输入 | 首次使用通过命令行交互式收集凭证 |
|
|
226
|
+
| 私钥存储方式 | 存储 PEM 内容而非文件路径 |
|
|
227
|
+
| Client 初始化 | 三个参数: `issuer_id`, `key_id`, `private_key` |
|
|
228
|
+
| Token 管理 | 由 `app_store_dev_api` gem 内部自动处理 JWT 生成 |
|
|
229
|
+
|
|
230
|
+
## 相关文档
|
|
231
|
+
|
|
232
|
+
- [创建 API 密钥](creating_api_keys.md)
|
|
233
|
+
- [生成 API 请求 Token](generating_tokens.md)
|
|
234
|
+
- [撤销 API 密钥](revoking_api_keys.md)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# 撤销 API 密钥
|
|
2
|
+
|
|
3
|
+
> 官方文档: https://developer.apple.com/documentation/appstoreconnectapi/revoking-api-keys
|
|
4
|
+
|
|
5
|
+
## 概述
|
|
6
|
+
|
|
7
|
+
当 API 密钥不再需要、可能泄露、或需要轮换时,应及时撤销。撤销后的密钥将永久失效,使用该密钥生成的所有 JWT Token 也将立即失效。
|
|
8
|
+
|
|
9
|
+
## 撤销步骤
|
|
10
|
+
|
|
11
|
+
### 撤销团队密钥
|
|
12
|
+
|
|
13
|
+
1. 登录 [App Store Connect](https://appstoreconnect.apple.com/)
|
|
14
|
+
2. 导航到 **用户和访问 (Users and Access)**
|
|
15
|
+
3. 选择 **集成 (Integrations)** 选项卡
|
|
16
|
+
4. 在左侧导航中选择 **App Store Connect API**
|
|
17
|
+
5. 选择 **团队密钥 (Team Keys)** 标签页
|
|
18
|
+
6. 找到要撤销的密钥
|
|
19
|
+
7. 点击密钥名称旁的 **撤销 (Revoke)** 按钮
|
|
20
|
+
8. 在确认对话框中确认撤销
|
|
21
|
+
|
|
22
|
+
### 撤销个人密钥
|
|
23
|
+
|
|
24
|
+
1. 登录 [App Store Connect](https://appstoreconnect.apple.com/)
|
|
25
|
+
2. 导航到 **用户和访问 (Users and Access)**
|
|
26
|
+
3. 选择 **集成 (Integrations)** 选项卡
|
|
27
|
+
4. 选择 **个人密钥 (Individual Keys)** 标签页
|
|
28
|
+
5. 找到要撤销的密钥并点击 **撤销 (Revoke)**
|
|
29
|
+
|
|
30
|
+
## 撤销后的影响
|
|
31
|
+
|
|
32
|
+
### 立即生效
|
|
33
|
+
|
|
34
|
+
- 密钥状态变为 **已撤销 (Revoked)**
|
|
35
|
+
- 使用该密钥签名的所有 JWT Token 立即失效
|
|
36
|
+
- 所有使用该 Token 的 API 请求将返回 `401 NOT_AUTHORIZED`
|
|
37
|
+
|
|
38
|
+
### 不可逆操作
|
|
39
|
+
|
|
40
|
+
> ⚠️ **警告**: 密钥撤销是**永久性**操作,无法恢复。一旦撤销,该密钥将无法重新激活。
|
|
41
|
+
|
|
42
|
+
- 已撤销的密钥仍会显示在密钥列表中(标记为已撤销),但无法使用
|
|
43
|
+
- 私钥文件(`.p8`)即使仍存在也无法再用于生成有效 Token
|
|
44
|
+
- Key ID 不会被回收利用
|
|
45
|
+
|
|
46
|
+
### 对正在运行的服务的影响
|
|
47
|
+
|
|
48
|
+
- 所有依赖该密钥的 CI/CD 流水线将立即中断
|
|
49
|
+
- 所有使用该密钥的自动化脚本将失败
|
|
50
|
+
- 所有后端服务的 API 调用将返回认证错误
|
|
51
|
+
|
|
52
|
+
## 何时应该撤销密钥
|
|
53
|
+
|
|
54
|
+
| 场景 | 紧急程度 | 建议操作 |
|
|
55
|
+
|------|----------|----------|
|
|
56
|
+
| 私钥文件泄露(提交到公开仓库等) | 🔴 立即 | 立刻撤销,创建新密钥 |
|
|
57
|
+
| 团队成员离职 | 🟡 尽快 | 撤销其创建的密钥 |
|
|
58
|
+
| 密钥定期轮换 | 🟢 计划中 | 先创建新密钥,迁移后再撤销旧密钥 |
|
|
59
|
+
| 密钥权限过高 | 🟡 尽快 | 创建权限适当的新密钥后撤销 |
|
|
60
|
+
| 密钥不再使用 | 🟢 常规 | 确认无依赖后撤销 |
|
|
61
|
+
|
|
62
|
+
## 安全的密钥轮换流程
|
|
63
|
+
|
|
64
|
+
为避免服务中断,建议按以下步骤进行密钥轮换:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
1. 创建新密钥
|
|
68
|
+
↓
|
|
69
|
+
2. 下载新私钥文件 (.p8)
|
|
70
|
+
↓
|
|
71
|
+
3. 更新所有服务/脚本使用新密钥
|
|
72
|
+
↓
|
|
73
|
+
4. 验证所有服务正常运行
|
|
74
|
+
↓
|
|
75
|
+
5. 撤销旧密钥
|
|
76
|
+
↓
|
|
77
|
+
6. 删除旧私钥文件
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 在本项目中轮换密钥
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
# 1. 使用新密钥创建客户端
|
|
84
|
+
new_client = AppStoreDevApi::Client.new(
|
|
85
|
+
issuer_id: ENV['APP_STORE_CONNECT_ISSUER_ID'], # Issuer ID 不变
|
|
86
|
+
key_id: 'NEW_KEY_ID',
|
|
87
|
+
private_key: File.read('/path/to/new_AuthKey.p8')
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# 2. 验证新密钥可用
|
|
91
|
+
apps = new_client.list_apps
|
|
92
|
+
puts "新密钥验证成功,共 #{apps['data'].size} 个应用"
|
|
93
|
+
|
|
94
|
+
# 3. 确认后,在 App Store Connect 中撤销旧密钥
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 常见问题
|
|
98
|
+
|
|
99
|
+
### 撤销密钥后可以恢复吗?
|
|
100
|
+
|
|
101
|
+
不可以。密钥撤销是永久性操作。如果误操作,只能创建新密钥。
|
|
102
|
+
|
|
103
|
+
### 撤销密钥会影响应用吗?
|
|
104
|
+
|
|
105
|
+
不会。撤销 API 密钥只影响 API 访问能力,不会影响已发布的应用、用户数据或 App Store 上架状态。
|
|
106
|
+
|
|
107
|
+
### 一个团队最多可以有多少个活跃密钥?
|
|
108
|
+
|
|
109
|
+
Apple 对每个团队的活跃 API 密钥数量有限制。建议只保留实际使用的密钥,及时撤销不再需要的密钥。
|
|
110
|
+
|
|
111
|
+
### 撤销后 Issuer ID 会变吗?
|
|
112
|
+
|
|
113
|
+
不会。Issuer ID 是团队级别的标识,不受单个密钥撤销的影响。
|
|
114
|
+
|
|
115
|
+
## 相关文档
|
|
116
|
+
|
|
117
|
+
- [创建 API 密钥](creating_api_keys.md)
|
|
118
|
+
- [生成 API 请求 Token](generating_tokens.md)
|
data/install_local.sh
CHANGED
|
@@ -1,84 +1,95 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# 简化的本地安装脚本 - 适用于库 gem 项目
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
#当前脚本目录 (macOS兼容)
|
|
4
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
5
|
+
CURRENT_DIR=$(dirname $(greadlink -f $0 2>/dev/null || readlink $0 2>/dev/null || echo $0))
|
|
6
|
+
else
|
|
7
|
+
CURRENT_DIR=$(dirname $(readlink -f $0))
|
|
8
|
+
fi
|
|
9
|
+
CURRENT_DIR=$(cd "$CURRENT_DIR" && pwd)
|
|
10
|
+
echo $CURRENT_DIR
|
|
11
|
+
|
|
5
12
|
|
|
6
|
-
#
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
# Make all files and directories in the current directory readable, writable, and executable for all users.
|
|
14
|
+
/bin/chmod -R a+rwx $CURRENT_DIR
|
|
15
|
+
# Change the owner of the current directory to the current user.
|
|
16
|
+
/usr/sbin/chown $(whoami) $CURRENT_DIR
|
|
17
|
+
# Change the group of the current directory to "admin".
|
|
18
|
+
/usr/bin/chgrp admin $CURRENT_DIR
|
|
9
19
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
function readConfigValue()
|
|
21
|
+
{
|
|
22
|
+
File=$1
|
|
23
|
+
KEY=$2
|
|
24
|
+
VALUE=$(grep -m 1 -o "^[ ]*$KEY[ ]*=[ ]*[\"]*.*[\"]*" $File | sed -e "s/^[ ]*$KEY[ ]*=[ ]*[\"]*//" -e "s/[\"]*$//")
|
|
25
|
+
echo $VALUE
|
|
26
|
+
}
|
|
14
27
|
|
|
15
|
-
|
|
16
|
-
|
|
28
|
+
|
|
29
|
+
# 自动检测当前项目名称
|
|
30
|
+
GEMSPEC_FILE=$(find $CURRENT_DIR -maxdepth 1 -name "*.gemspec" | head -n 1)
|
|
17
31
|
if [ -z "$GEMSPEC_FILE" ]; then
|
|
18
|
-
echo "
|
|
32
|
+
echo "错误: 未找到gemspec文件!"
|
|
19
33
|
exit 1
|
|
20
34
|
fi
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
# 从gemspec文件名获取项目名称
|
|
37
|
+
PROJECT_NAME=$(basename $GEMSPEC_FILE .gemspec)
|
|
38
|
+
echo "检测到项目: $PROJECT_NAME"
|
|
39
|
+
|
|
40
|
+
# 清理旧的gem文件
|
|
41
|
+
rm -rf $CURRENT_DIR/*${PROJECT_NAME}-*.gem
|
|
24
42
|
|
|
25
|
-
#
|
|
26
|
-
VERSION_FILE=$(find
|
|
43
|
+
# 自动检测版本文件
|
|
44
|
+
VERSION_FILE=$(find $CURRENT_DIR/lib -name "version.rb" | head -n 1)
|
|
27
45
|
if [ -z "$VERSION_FILE" ]; then
|
|
28
|
-
echo "
|
|
46
|
+
echo "错误: 未找到version.rb文件!"
|
|
29
47
|
exit 1
|
|
30
48
|
fi
|
|
31
49
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
echo "检测到版本文件: $VERSION_FILE"
|
|
51
|
+
VERSION_KEY="VERSION"
|
|
52
|
+
# 改进版本号提取方式,确保只获取版本号部分
|
|
53
|
+
VERSION_TAG_NAME=$(grep -o "${VERSION_KEY}.*=.*['\"]\(.*\)['\"]" ${VERSION_FILE} | head -1 | sed -E "s/.*['\"]([0-9]+\.[0-9]+\.[0-9]+)['\"].*/\1/")
|
|
54
|
+
echo "检测到版本: $VERSION_TAG_NAME"
|
|
55
|
+
|
|
56
|
+
# 验证版本号格式
|
|
57
|
+
if [[ ! $VERSION_TAG_NAME =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
58
|
+
echo "错误: 无法正确解析版本号,获取到的值: '$VERSION_TAG_NAME'"
|
|
59
|
+
echo "直接查看版本文件内容:"
|
|
60
|
+
cat $VERSION_FILE
|
|
38
61
|
exit 1
|
|
39
62
|
fi
|
|
40
63
|
|
|
41
|
-
echo "版本: $VERSION"
|
|
42
|
-
echo
|
|
43
|
-
|
|
44
|
-
# 3. 清理旧的 gem 文件
|
|
45
|
-
echo "清理旧的 gem 文件..."
|
|
46
|
-
rm -f *.gem
|
|
47
|
-
echo "✓ 清理完成"
|
|
48
|
-
echo
|
|
49
64
|
|
|
50
|
-
#
|
|
51
|
-
echo "构建 gem..."
|
|
52
|
-
gem build
|
|
65
|
+
# 构建和安装gem
|
|
66
|
+
echo "构建 $PROJECT_NAME gem..."
|
|
67
|
+
gem build $GEMSPEC_FILE
|
|
53
68
|
|
|
54
|
-
|
|
69
|
+
# 查找生成的gem文件(在当前目录和CURRENT_DIR中查找)
|
|
70
|
+
GEM_FILE=$(find . -maxdepth 1 -name "${PROJECT_NAME}-${VERSION_TAG_NAME}.gem" | head -n 1)
|
|
71
|
+
if [ -z "$GEM_FILE" ]; then
|
|
72
|
+
GEM_FILE=$(find $CURRENT_DIR -maxdepth 1 -name "${PROJECT_NAME}-${VERSION_TAG_NAME}.gem" | head -n 1)
|
|
73
|
+
fi
|
|
55
74
|
|
|
56
|
-
if [
|
|
57
|
-
echo "
|
|
58
|
-
|
|
75
|
+
if [ -n "$GEM_FILE" ]; then
|
|
76
|
+
echo "找到gem文件: $GEM_FILE"
|
|
77
|
+
echo "安装 $PROJECT_NAME-${VERSION_TAG_NAME}.gem..."
|
|
78
|
+
gem install --local "$GEM_FILE"
|
|
79
|
+
else
|
|
80
|
+
echo "错误: 未找到生成的gem文件! 尝试查找任何版本的gem..."
|
|
81
|
+
ANY_GEM=$(find . -maxdepth 1 -name "${PROJECT_NAME}-*.gem" | head -n 1)
|
|
82
|
+
if [ -n "$ANY_GEM" ]; then
|
|
83
|
+
echo "找到gem文件: $ANY_GEM"
|
|
84
|
+
echo "安装 $ANY_GEM..."
|
|
85
|
+
gem install --local "$ANY_GEM"
|
|
86
|
+
else
|
|
87
|
+
echo "未找到任何${PROJECT_NAME}的gem文件"
|
|
88
|
+
fi
|
|
59
89
|
fi
|
|
60
90
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
gem install "$GEM_FILE"
|
|
67
|
-
|
|
68
|
-
echo
|
|
69
|
-
echo "=========================================="
|
|
70
|
-
echo "✅ 安装成功!"
|
|
71
|
-
echo "=========================================="
|
|
72
|
-
echo
|
|
73
|
-
echo "使用方法 (Ruby 代码中):"
|
|
74
|
-
echo " require 'app_store_dev_api'"
|
|
75
|
-
echo " client = AppStoreDevApi::Client.new("
|
|
76
|
-
echo " key_id: 'YOUR_KEY_ID',"
|
|
77
|
-
echo " issuer_id: 'YOUR_ISSUER_ID',"
|
|
78
|
-
echo " private_key: File.read('AuthKey.p8')"
|
|
79
|
-
echo " )"
|
|
80
|
-
echo
|
|
81
|
-
echo "验证安装:"
|
|
82
|
-
echo " gem list | grep $PROJECT_NAME"
|
|
83
|
-
echo " ruby -r app_store_dev_api -e 'puts AppStoreDevApi::VERSION'"
|
|
84
|
-
echo "=========================================="
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
data/lib/.DS_Store
CHANGED
|
Binary file
|
|
@@ -9,7 +9,7 @@ module AppStoreDevApi
|
|
|
9
9
|
|
|
10
10
|
DEFAULTS = {
|
|
11
11
|
analytics_enabled: true,
|
|
12
|
-
schema: Schema.new(File.join(__dir__, '..', '..', 'config', 'schema_v4.
|
|
12
|
+
schema: Schema.new(File.join(__dir__, '..', '..', 'config', 'schema_v4.3.json'))
|
|
13
13
|
}.freeze
|
|
14
14
|
private_constant :DEFAULTS
|
|
15
15
|
|
|
@@ -4602,16 +4602,6 @@
|
|
|
4602
4602
|
"url": "https://api.appstoreconnect.apple.com/v1/appStoreVersionLocalizations/{id}/searchKeywords",
|
|
4603
4603
|
"alias": "app_store_version_localization_search_keywords"
|
|
4604
4604
|
},
|
|
4605
|
-
{
|
|
4606
|
-
"http_method": "get",
|
|
4607
|
-
"url": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/{id}/relationships/ageRatingDeclaration",
|
|
4608
|
-
"alias": "app_store_version_relationships_age_rating_declaration"
|
|
4609
|
-
},
|
|
4610
|
-
{
|
|
4611
|
-
"http_method": "get",
|
|
4612
|
-
"url": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/{id}/ageRatingDeclaration",
|
|
4613
|
-
"alias": "app_store_version_age_rating_declaration"
|
|
4614
|
-
},
|
|
4615
4605
|
{
|
|
4616
4606
|
"http_method": "get",
|
|
4617
4607
|
"url": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/{id}/relationships/alternativeDistributionPackage",
|
data/push_all.sh
ADDED
data/release_remote.sh
CHANGED
|
@@ -21,10 +21,29 @@ function readConfigValue()
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
# 自动检测当前项目名称
|
|
25
|
+
GEMSPEC_FILE=$(find $CURRENT_DIR -maxdepth 1 -name "*.gemspec" | head -n 1)
|
|
26
|
+
if [ -z "$GEMSPEC_FILE" ]; then
|
|
27
|
+
echo "错误: 未找到gemspec文件!"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# 从gemspec文件名获取项目名称
|
|
32
|
+
PROJECT_NAME=$(basename $GEMSPEC_FILE .gemspec)
|
|
33
|
+
echo "检测到项目: $PROJECT_NAME"
|
|
34
|
+
|
|
35
|
+
# 清理旧的gem文件
|
|
36
|
+
rm -rf $CURRENT_DIR/*${PROJECT_NAME}-*.gem
|
|
37
|
+
|
|
38
|
+
# 自动检测版本文件
|
|
39
|
+
VERSION_FILE=$(find $CURRENT_DIR/lib -name "version.rb" | head -n 1)
|
|
40
|
+
if [ -z "$VERSION_FILE" ]; then
|
|
41
|
+
echo "错误: 未找到version.rb文件!"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
25
44
|
|
|
26
|
-
|
|
27
|
-
VERSION_TAG_NAME=$(grep
|
|
45
|
+
VERSION_KEY="VERSION"
|
|
46
|
+
VERSION_TAG_NAME=$(grep -o "${VERSION_KEY}.*=.*['\"]\(.*\)['\"]" ${VERSION_FILE} | head -1 | sed -E "s/.*['\"]([0-9]+\.[0-9]+\.[0-9]+)['\"].*/\1/")
|
|
28
47
|
TAG_NAME="v${VERSION_TAG_NAME}"
|
|
29
48
|
|
|
30
49
|
COMMIT_FILE_LIST=$(git -C $CURRENT_DIR ls-files --other --modified --exclude-standard)
|
|
@@ -61,9 +80,9 @@ fi
|
|
|
61
80
|
|
|
62
81
|
|
|
63
82
|
|
|
64
|
-
gem build $
|
|
65
|
-
if [[ -f $CURRENT_DIR
|
|
66
|
-
gem install --local $CURRENT_DIR
|
|
83
|
+
gem build $GEMSPEC_FILE
|
|
84
|
+
if [[ -f $CURRENT_DIR/${PROJECT_NAME}-${VERSION_TAG_NAME}.gem ]]; then
|
|
85
|
+
gem install --local $CURRENT_DIR/${PROJECT_NAME}-${VERSION_TAG_NAME}.gem
|
|
67
86
|
fi
|
|
68
87
|
|
|
69
88
|
|
|
@@ -108,8 +127,8 @@ git -C $CURRENT_DIR push origin ${TAG_NAME}
|
|
|
108
127
|
git -C $CURRENT_DIR checkout $CODING_BRANCH
|
|
109
128
|
|
|
110
129
|
|
|
111
|
-
if [[ -f $CURRENT_DIR
|
|
112
|
-
gem push $CURRENT_DIR
|
|
130
|
+
if [[ -f $CURRENT_DIR/${PROJECT_NAME}-${VERSION_TAG_NAME}.gem ]]; then
|
|
131
|
+
gem push $CURRENT_DIR/${PROJECT_NAME}-${VERSION_TAG_NAME}.gem
|
|
113
132
|
fi
|
|
114
133
|
|
|
115
134
|
|
|
@@ -17,7 +17,7 @@ openapi_paths = openapi_data['paths']
|
|
|
17
17
|
|
|
18
18
|
# 2. 读取 schema.json 配置
|
|
19
19
|
schema_files = [
|
|
20
|
-
File.join(__dir__, '../lib/config/schema_v4.
|
|
20
|
+
File.join(__dir__, '../lib/config/schema_v4.3.json'),
|
|
21
21
|
File.join(__dir__, '../lib/config/schema.json')
|
|
22
22
|
].find { |f| File.exist?(f) }
|
|
23
23
|
|
|
@@ -15,7 +15,7 @@ puts
|
|
|
15
15
|
openapi_file = 'docs/openapi.oas4.2.json'
|
|
16
16
|
openapi_data = JSON.parse(File.read(openapi_file))
|
|
17
17
|
|
|
18
|
-
schema_file = 'lib/config/schema_v4.
|
|
18
|
+
schema_file = 'lib/config/schema_v4.3.json'
|
|
19
19
|
schema_data = JSON.parse(File.read(schema_file))
|
|
20
20
|
|
|
21
21
|
request_files = Dir.glob('lib/app_store_dev_api/requests/**/{create,update}.rb')
|