fastlane-plugin-upload_to_ru_store 1.0.7 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc76c3019ad3d57eb83453bfdab9654bd5de46db1e7cb3962a490a98ef4adaed
4
- data.tar.gz: 5cff3342df66967227d99851b86689e563d35c4fb6c045ee6e184bc90a1bf213
3
+ metadata.gz: fe89fe301094e12993917fddd33e1d04d563fe9b7d300f72503f045afb3eda8b
4
+ data.tar.gz: 9e52866000cb8b048a48994dd1772efc5bd3972df9dc5180efda941b267dc2a3
5
5
  SHA512:
6
- metadata.gz: 0753a58fbd5f182811f6de99f09770c6d08780986d0ca0680642329fd417abe515d2d2d91960178925ea26dc949ed9f0bb44449c82afb685982965bbd3cc4078
7
- data.tar.gz: b7d68ad367b15ebd990d0c01442cb52df8e9276860deafd4ca3ccd82b1db1f060914e840470b45e0b50ba75e9fd7280f58ced3ad25089d8d9855065b8c93766f
6
+ metadata.gz: 6ceff64b463b21b7a627ce1b5307e82891200eb1db96643f18e44d542ac1a20eb860fbe144928a0e188df8a8dfee2d7a5dc6625eb4692730f7a1178df57250bf
7
+ data.tar.gz: 158f54c8414af4498c9929612cd7acf66d79bc4c05188ccfe9a025c5a2220b0ac93f0a49109ac38870c838ea2d455bfc8f7184735f1336d0dbd2a7b8a0a249eb
data/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ # Changelog
2
+
3
+ Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
4
+ версии следуют [Semantic Versioning](https://semver.org/lang/ru/).
5
+
6
+ ## [1.1.0] — 2026-09-19
7
+
8
+ ### Добавлено
9
+ - Действие `rustore_version_status` — статус последней версии приложения.
10
+ - Действие `rustore_publish_version` — ручная публикация прошедшей модерацию версии.
11
+ - Действие `rustore_archive_version` — архивирование версии.
12
+ - Действие `rustore_publish_settings` — изменение типа публикации, даты и процента раскатки.
13
+ - Поэтапная раскатка через `partial_value` (5/10/25/50/75/100).
14
+ - Метаданные черновика: `app_name`, `app_type`, `age_legal`, `short_description`,
15
+ `full_description`, `moder_info`, `price_value`, `whats_new`.
16
+ - Флаги `remove_existing_drafts` и `submit_for_moderation`, параметр `priority_update`.
17
+ - Настраиваемый `timeout` и повтор запроса при сетевых сбоях.
18
+ - `RUSTORE_VERSION_ID` и `RUSTORE_VERSION_STATUS` в `lane_context`.
19
+
20
+ ### Исправлено
21
+ - `publish_datetime` не передавался в запрос создания черновика, из-за чего
22
+ `publish_type: "DELAYED"` всегда завершался ошибкой.
23
+ - Плагин не загружался вместе с современным fastlane: `faraday_middleware`
24
+ требует Faraday 1.x, а fastlane использует Faraday 2.x.
25
+ - В gemspec не было ни одной runtime-зависимости, поэтому в чистом окружении
26
+ плагин падал с `cannot load such file`.
27
+ - Токен `Public-Token` попадал в stderr при каждом запуске: HTTP-логгер был
28
+ включён безусловно. Теперь он включается через `RUSTORE_DEBUG`, а токен,
29
+ подпись и JWE маскируются.
30
+ - `key_id` и `private_key` не были помечены `sensitive`, из-за чего fastlane
31
+ печатал их в сводке параметров.
32
+ - Лимит changelog был 500 символов вместо документированных 5000.
33
+ - Ответы не-JSON (HTML-страницы ошибок, 502 от прокси) роняли плагин на `#dig`.
34
+ - Список черновиков читался только с первой страницы, остальные не удалялись.
35
+ - Статус HTTP не проверялся: сбой публикации выглядел как успех.
36
+ - Ключи PKCS#8 в виде голого Base64 не читались.
37
+ - Отсутствовала проверка существования файлов сборки и changelog.
38
+
39
+ ### Изменено
40
+ - Код разделён на слои: `Rustore::Signer`, `Rustore::Client`, `Rustore::Api`
41
+ и фасад `UploadToRuStoreHelper`, сохраняющий прежний публичный интерфейс.
42
+ - Минимальная версия Ruby — 3.2: зависимости fastlane (google-apis-*, googleauth,
43
+ signet, excon) больше не собираются на 3.1.
44
+ - CI переведён на Ruby 3.2/3.3/3.4 и `actions/checkout@v4`.
45
+
46
+ ## [1.0.7]
47
+
48
+ - Увеличен таймаут запросов.
data/README.md CHANGED
@@ -1,28 +1,43 @@
1
- # upload_to_ru_store
1
+ # fastlane-plugin-upload_to_ru_store
2
2
 
3
3
  [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-upload_to_ru_store)
4
4
 
5
- **Fastlane-плагин для автоматической публикации Android-приложений (AAB/APK) в RuStore.**
5
+ Fastlane-плагин для публикации Android-приложений в **RuStore**: загрузка AAB/APK,
6
+ поэтапная раскатка, отложенный релиз, ручная публикация и архивирование версий.
6
7
 
7
- За основу взять плагин от Vladislav Onishchenko: https://github.com/stfbee/fastlane-plugin-rustore
8
+ ---
8
9
 
9
- ## Возможности
10
+ ## Содержание
10
11
 
11
- - Аутентификация через RSA-SHA512 и получение JWE-токена RuStore API
12
- - Автоматическая очистка всех незавершённых черновиков (draft) перед созданием новой версии
13
- - Создание драфта с указанием типа публикации и текста «Что нового»
14
- - Загрузка AAB или APK (GMS и/или HMS) в один или два шага
15
- - Фиксация (commit) драфта и отправка на модерацию
12
+ - [Установка](#установка)
13
+ - [Быстрый старт](#быстрый-старт)
14
+ - [Получение ключей](#получение-ключей)
15
+ - [Действия](#действия)
16
+ - [upload_to_ru_store](#upload_to_ru_store)
17
+ - [rustore_version_status](#rustore_version_status)
18
+ - [rustore_publish_version](#rustore_publish_version)
19
+ - [rustore_publish_settings](#rustore_publish_settings)
20
+ - [rustore_archive_version](#rustore_archive_version)
21
+ - [Рецепты](#рецепты)
22
+ - [Переменные окружения](#переменные-окружения)
23
+ - [Отладка](#отладка)
24
+ - [Ограничения RuStore](#ограничения-rustore)
25
+ - [Обновление с 1.0.x](#обновление-с-10x)
26
+ - [Разработка](#разработка)
16
27
 
17
- ## Установка
28
+ ---
18
29
 
19
- В каталоге с вашим Fastfile выполните:
30
+ ## Установка
20
31
 
21
32
  ```bash
22
33
  fastlane add_plugin upload_to_ru_store
23
34
  ```
24
35
 
25
- ## Пример использования
36
+ Требуется Ruby >= 3.2 и fastlane >= 2.240.
37
+
38
+ ---
39
+
40
+ ## Быстрый старт
26
41
 
27
42
  ```ruby
28
43
  lane :publish_to_rustore do
@@ -30,47 +45,379 @@ lane :publish_to_rustore do
30
45
  package_name: "com.example.app",
31
46
  key_id: ENV["RUSTORE_KEY_ID"],
32
47
  private_key: ENV["RUSTORE_PRIVATE_KEY"],
33
- publish_type: "INSTANTLY", # MANUAL, DELAYED или INSTANTLY (по умолчанию INSTANTLY)
34
- changelog_path: "metadata/android/ru-RU/changelog.txt", # опционально, макс. 500 символов
35
- aab: "app/build/outputs/bundle/release/app-release.aab", # если указан, зальётся только AAB
36
- gms_apk: "app/build/outputs/apk/release/app-release.apk", # путь к Google-APK (если не указан AAB)
37
- hms_apk: "app-huawei-release.apk" # путь к Huawei-APK (опционально)
48
+ aab: "app/build/outputs/bundle/release/app-release.aab",
49
+ changelog_path: "metadata/android/ru-RU/changelog.txt"
38
50
  )
39
51
  end
40
52
  ```
41
53
 
42
- **Плагин автоматически:**
54
+ Что происходит под капотом:
55
+
56
+ 1. подпись `key_id + timestamp` по RSA-SHA512, обмен на JWE-токен;
57
+ 2. удаление незавершённых черновиков приложения;
58
+ 3. создание черновика с метаданными и настройками публикации;
59
+ 4. загрузка сборки (AAB, либо GMS APK и опционально HMS APK);
60
+ 5. отправка черновика на модерацию.
61
+
62
+ Шаги 2 и 5 отключаются флагами `remove_existing_drafts` и `submit_for_moderation`.
63
+
64
+ ---
65
+
66
+ ## Получение ключей
67
+
68
+ В консоли RuStore: **Настройки → Ключи доступа → Создать ключ**. Вы получите
69
+ идентификатор ключа (`key_id`) и приватный RSA-ключ.
70
+
71
+ `private_key` принимается в любом из форматов:
72
+
73
+ - PEM с заголовком `-----BEGIN PRIVATE KEY-----` (PKCS#8);
74
+ - PEM с заголовком `-----BEGIN RSA PRIVATE KEY-----` (PKCS#1);
75
+ - голый Base64 без заголовков — формат определяется автоматически по структуре DER.
76
+
77
+ Последний вариант удобен для CI: ключ кладётся в секрет одной строкой.
78
+
79
+ ```bash
80
+ RUSTORE_PRIVATE_KEY=$(tr -d '\n' < key.pem)
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Действия
86
+
87
+ ### upload_to_ru_store
43
88
 
44
- 1. Получит JWE-токен RuStore
45
- 2. Удалит все существующие черновики приложения
46
- 3. Создаст новый драфт
47
- 4. Загрузит указанные сборки (AAB и/или APK)
48
- 5. Закоммитит драфт и отправит на модерацию
89
+ Полный цикл выпуска: черновик → сборка → модерация. Возвращает `versionId`
90
+ созданного черновика и кладёт его в `lane_context[SharedValues::RUSTORE_VERSION_ID]`.
49
91
 
50
- ## Опции
92
+ #### Обязательные параметры
93
+
94
+ | Параметр | Описание |
95
+ |---|---|
96
+ | `package_name` | Идентификатор пакета, например `com.example.app` |
97
+ | `key_id` | Идентификатор ключа доступа RuStore |
98
+ | `private_key` | Приватный RSA-ключ |
99
+
100
+ Нужно указать хотя бы `aab` или `gms_apk`.
101
+
102
+ #### Сборки
103
+
104
+ | Параметр | Описание |
105
+ |---|---|
106
+ | `aab` | Путь к Android App Bundle. Если указан, APK не загружаются |
107
+ | `gms_apk` | Путь к APK с Google Mobile Services |
108
+ | `hms_apk` | Путь к APK с Huawei Mobile Services, грузится дополнительно |
109
+
110
+ #### Публикация
111
+
112
+ | Параметр | Значения | Описание |
113
+ |---|---|---|
114
+ | `publish_type` | `MANUAL`, `INSTANTLY`, `DELAYED` | Тип публикации |
115
+ | `publish_datetime` | ISO8601 | Обязателен при `DELAYED`. От 24 часов до 60 дней от момента отправки на модерацию |
116
+ | `partial_value` | `5`, `10`, `25`, `50`, `75`, `100` | Процент поэтапной раскатки |
117
+ | `priority_update` | целое число | Приоритет обновления при отправке на модерацию |
118
+
119
+ #### Метаданные черновика
120
+
121
+ | Параметр | Лимит | Описание |
122
+ |---|---|---|
123
+ | `changelog_path` | 5000 символов | Путь к файлу «Что нового» |
124
+ | `whats_new` | 5000 символов | То же самое, но строкой |
125
+ | `app_name` | 50 символов | Название приложения |
126
+ | `app_type` | `MAIN`, `GAMES` | Тип приложения |
127
+ | `age_legal` | `0+`, `6+`, `12+`, `16+`, `18+` | Возрастной рейтинг |
128
+ | `short_description` | 80 символов | Краткое описание |
129
+ | `full_description` | 4000 символов | Полное описание |
130
+ | `moder_info` | 180 символов | Комментарий для модератора |
131
+ | `price_value` | целое | Цена в копейках для платных приложений |
132
+
133
+ #### Поведение
134
+
135
+ | Параметр | По умолчанию | Описание |
136
+ |---|---|---|
137
+ | `remove_existing_drafts` | `true` | Удалять существующие черновики перед созданием нового |
138
+ | `submit_for_moderation` | `true` | Отправлять черновик на модерацию |
139
+ | `timeout` | `600` | Таймаут чтения ответа, секунды |
140
+
141
+ ---
142
+
143
+ ### rustore_version_status
144
+
145
+ Возвращает Hash с данными последней версии: `versionId`, `versionCode`,
146
+ `versionName`, `versionStatus`, `publishType`, `partialValue` и остальные поля RuStore.
147
+ Если версий нет — возвращает `nil`.
148
+
149
+ ```ruby
150
+ version = rustore_version_status(
151
+ package_name: "com.example.app",
152
+ key_id: ENV["RUSTORE_KEY_ID"],
153
+ private_key: ENV["RUSTORE_PRIVATE_KEY"]
154
+ )
155
+
156
+ UI.message("Статус: #{version['versionStatus']}")
157
+ ```
158
+
159
+ Параметры: `package_name`, `key_id`, `private_key`, `testing_type` (`ALL`/`RELEASE`/`ALPHA`), `timeout`.
160
+
161
+ Возможные значения `versionStatus`:
162
+
163
+ | Статус | Значение |
164
+ |---|---|
165
+ | `DRAFT` | черновик |
166
+ | `AUTO_CHECK` | антивирусная проверка |
167
+ | `AUTO_CHECK_FAILED` | не прошла антивирусную проверку |
168
+ | `MODERATION` | ждёт модерации |
169
+ | `TAKEN_FOR_MODERATION` | взята модератором |
170
+ | `READY_FOR_PUBLICATION` | прошла модерацию |
171
+ | `REJECTED_BY_MODERATOR` | отклонена модератором |
172
+ | `REJECTED_BY_SECURITY` | отклонена службой безопасности |
173
+ | `ACTIVE` | опубликована |
174
+ | `PARTIAL_ACTIVE` | опубликована частично |
175
+ | `PREVIOUS_ACTIVE` | предыдущая активная версия |
176
+ | `ARCHIVED` | архивирована |
177
+ | `DELETED_DRAFT` | удалённый черновик |
178
+
179
+ ---
180
+
181
+ ### rustore_publish_version
182
+
183
+ Публикует версию, которая прошла модерацию и была создана с `publish_type: "MANUAL"`.
184
+ Без `version_id` берёт последнюю версию приложения.
185
+
186
+ ```ruby
187
+ rustore_publish_version(
188
+ package_name: "com.example.app",
189
+ key_id: ENV["RUSTORE_KEY_ID"],
190
+ private_key: ENV["RUSTORE_PRIVATE_KEY"]
191
+ )
192
+ ```
193
+
194
+ ---
195
+
196
+ ### rustore_publish_settings
197
+
198
+ Меняет настройки публикации уже существующей версии: тип, дату отложенного
199
+ релиза, процент раскатки.
200
+
201
+ ```ruby
202
+ rustore_publish_settings(
203
+ package_name: "com.example.app",
204
+ key_id: ENV["RUSTORE_KEY_ID"],
205
+ private_key: ENV["RUSTORE_PRIVATE_KEY"],
206
+ version_id: 12345,
207
+ partial_value: 50
208
+ )
209
+ ```
210
+
211
+ RuStore разрешает только увеличивать процент раскатки; `100` означает полный релиз.
212
+
213
+ ---
214
+
215
+ ### rustore_archive_version
216
+
217
+ Архивирует версию. RuStore принимает архивирование только для статусов
218
+ `READY_FOR_PUBLICATION` и `REJECTED_BY_MODERATOR`.
219
+
220
+ ```ruby
221
+ rustore_archive_version(
222
+ package_name: "com.example.app",
223
+ key_id: ENV["RUSTORE_KEY_ID"],
224
+ private_key: ENV["RUSTORE_PRIVATE_KEY"],
225
+ version_id: 12345
226
+ )
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Рецепты
232
+
233
+ ### Поэтапная раскатка
234
+
235
+ Выпуск на 10% аудитории, затем расширение:
236
+
237
+ ```ruby
238
+ lane :rollout_start do
239
+ upload_to_ru_store(
240
+ package_name: "com.example.app",
241
+ key_id: ENV["RUSTORE_KEY_ID"],
242
+ private_key: ENV["RUSTORE_PRIVATE_KEY"],
243
+ aab: "app-release.aab",
244
+ publish_type: "INSTANTLY",
245
+ partial_value: 10
246
+ )
247
+ end
51
248
 
52
- | Параметр | Описание | Обязательный | Формат |
53
- |--------------------|--------------------------------------------------------------------------------|----------------------------------------|----------|
54
- | `package_name` | Уникальный идентификатор пакета (например, `com.example.app`) | да | `String` |
55
- | `key_id` | Идентификатор RSA-ключа в консоли RuStore | да | `String` |
56
- | `private_key` | PEM-строка RSA-приватного ключа | да | `String` |
57
- | `publish_type` | Тип публикации: `MANUAL`, `DELAYED` или `INSTANTLY` (по умолчанию `INSTANTLY`) | нет | `String` |
58
- | `publish_datetime` | Дата и время для отложенной публикации, ISO8601 | да, только если publish_type = DELAYED | `String` |
59
- | `changelog_path` | Путь к `.txt`-файлу с описанием «Что нового?» (макс. 500 символов) | нет | `String` |
60
- | `aab` | Путь к Android App Bundle (`.aab`). Если указан, APK не загружается | нет | `String` |
61
- | `gms_apk` | Путь к APK с Google Mobile Services. Используется, если не указан `aab` | нет | `String` |
62
- | `hms_apk` | Путь к APK с Huawei Mobile Services | нет | `String` |
249
+ lane :rollout_expand do |options|
250
+ version = rustore_version_status(
251
+ package_name: "com.example.app",
252
+ key_id: ENV["RUSTORE_KEY_ID"],
253
+ private_key: ENV["RUSTORE_PRIVATE_KEY"]
254
+ )
255
+
256
+ rustore_publish_settings(
257
+ package_name: "com.example.app",
258
+ key_id: ENV["RUSTORE_KEY_ID"],
259
+ private_key: ENV["RUSTORE_PRIVATE_KEY"],
260
+ version_id: version["versionId"],
261
+ partial_value: options[:percent].to_i
262
+ )
263
+ end
264
+ ```
265
+
266
+ ### Отложенная публикация
267
+
268
+ ```ruby
269
+ upload_to_ru_store(
270
+ package_name: "com.example.app",
271
+ key_id: ENV["RUSTORE_KEY_ID"],
272
+ private_key: ENV["RUSTORE_PRIVATE_KEY"],
273
+ aab: "app-release.aab",
274
+ publish_type: "DELAYED",
275
+ publish_datetime: (Time.now + 3 * 24 * 3600).iso8601
276
+ )
277
+ ```
278
+
279
+ ### Ручная публикация после проверки
280
+
281
+ ```ruby
282
+ lane :stage do
283
+ upload_to_ru_store(
284
+ package_name: "com.example.app",
285
+ key_id: ENV["RUSTORE_KEY_ID"],
286
+ private_key: ENV["RUSTORE_PRIVATE_KEY"],
287
+ aab: "app-release.aab",
288
+ publish_type: "MANUAL"
289
+ )
290
+ end
291
+
292
+ lane :go_live do
293
+ version = rustore_version_status(
294
+ package_name: "com.example.app",
295
+ key_id: ENV["RUSTORE_KEY_ID"],
296
+ private_key: ENV["RUSTORE_PRIVATE_KEY"]
297
+ )
298
+
299
+ UI.user_error!("Версия ещё не прошла модерацию: #{version['versionStatus']}") unless
300
+ version["versionStatus"] == "READY_FOR_PUBLICATION"
301
+
302
+ rustore_publish_version(
303
+ package_name: "com.example.app",
304
+ key_id: ENV["RUSTORE_KEY_ID"],
305
+ private_key: ENV["RUSTORE_PRIVATE_KEY"],
306
+ version_id: version["versionId"]
307
+ )
308
+ end
309
+ ```
310
+
311
+ ### Черновик без отправки на модерацию
312
+
313
+ Полезно, когда сборку нужно залить заранее, а метаданные дозаполнить в консоли.
314
+
315
+ ```ruby
316
+ upload_to_ru_store(
317
+ package_name: "com.example.app",
318
+ key_id: ENV["RUSTORE_KEY_ID"],
319
+ private_key: ENV["RUSTORE_PRIVATE_KEY"],
320
+ aab: "app-release.aab",
321
+ submit_for_moderation: false
322
+ )
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Переменные окружения
328
+
329
+ Каждый параметр читается из переменной окружения, что удобно в CI:
330
+
331
+ | Переменная | Параметр |
332
+ |---|---|
333
+ | `RUSTORE_PACKAGE_NAME` | `package_name` |
334
+ | `RUSTORE_KEY_ID` | `key_id` |
335
+ | `RUSTORE_PRIVATE_KEY` | `private_key` |
336
+ | `RUSTORE_PUBLISH_TYPE` | `publish_type` |
337
+ | `RUSTORE_PUBLISH_DATETIME` | `publish_datetime` |
338
+ | `RUSTORE_PARTIAL_VALUE` | `partial_value` |
339
+ | `RUSTORE_AAB` | `aab` |
340
+ | `RUSTORE_GMS_APK` | `gms_apk` |
341
+ | `RUSTORE_HMS_APK` | `hms_apk` |
342
+ | `RUSTORE_CHANGELOG_PATH` | `changelog_path` |
343
+ | `RUSTORE_VERSION_ID` | `version_id` |
344
+ | `RUSTORE_TESTING_TYPE` | `testing_type` |
345
+ | `RUSTORE_TIMEOUT` | `timeout` |
346
+
347
+ `key_id` и `private_key` помечены как `sensitive`, поэтому fastlane не печатает
348
+ их в сводке параметров.
349
+
350
+ ---
351
+
352
+ ## Отладка
353
+
354
+ ```bash
355
+ RUSTORE_DEBUG=1 bundle exec fastlane publish_to_rustore
356
+ ```
357
+
358
+ Включает подробный HTTP-лог. Заголовок `Public-Token`, поле `signature` и `jwe`
359
+ в выводе маскируются.
360
+
361
+ При сетевых сбоях и ответах 429/5xx запрос повторяется до трёх раз с
362
+ экспоненциальной паузой.
363
+
364
+ ---
365
+
366
+ ## Ограничения RuStore
367
+
368
+ - Загрузить сборку можно только с `versionCode` больше предыдущего.
369
+ - Отложенная публикация — не раньше чем через 24 часа и не позже чем через 60 дней.
370
+ - Процент раскатки можно только увеличивать.
371
+ - Архивировать можно только версии в статусах `READY_FOR_PUBLICATION` и `REJECTED_BY_MODERATOR`.
372
+ - Публиковать вручную можно только версию, прошедшую модерацию с типом `MANUAL`.
373
+
374
+ ---
375
+
376
+ ## Обновление с 1.0.x
377
+
378
+ Существующие вызовы `upload_to_ru_store` продолжают работать без изменений.
379
+ На что стоит обратить внимание:
380
+
381
+ - `publish_type: "DELAYED"` наконец работает — в 1.0.x `publish_datetime`
382
+ не доходил до API, и вызов всегда падал;
383
+ - лимит changelog поднят с 500 до документированных API 5000 символов;
384
+ - минимальная версия Ruby — 3.2: дерево зависимостей fastlane требует её;
385
+ - HTTP-лог больше не печатается без запроса, включается через `RUSTORE_DEBUG`.
386
+
387
+ ---
388
+
389
+ ## Разработка
390
+
391
+ ```bash
392
+ bundle install
393
+ bundle exec rspec
394
+ bundle exec rubocop
395
+ ```
396
+
397
+ Структура:
398
+
399
+ ```
400
+ lib/fastlane/plugin/upload_to_ru_store/
401
+ ├── actions/ действия fastlane
402
+ └── helper/
403
+ ├── upload_to_ru_store_helper.rb фасад, сохраняющий обратную совместимость
404
+ └── rustore/
405
+ ├── signer.rb загрузка ключа и подпись RSA-SHA512
406
+ ├── client.rb HTTP-транспорт, повторы, маскирование секретов
407
+ ├── api.rb методы RuStore API и валидация полей
408
+ ├── options.rb переиспользуемые ConfigItem
409
+ └── errors.rb типизированные ошибки
410
+ ```
63
411
 
64
- ## Требования
412
+ Сетевые вызовы в тестах замоканы через `webmock`, реальные соединения запрещены.
65
413
 
66
- - Ruby >= 2.6
67
- - Fastlane >= 2.214.0
414
+ ---
68
415
 
69
416
  ## Лицензия
70
417
 
71
- MIT. Смотрите [LICENSE](LICENSE)
418
+ MIT, см. [LICENSE](LICENSE).
72
419
 
73
- ## Полезные ссылки
420
+ ## Ссылки
74
421
 
75
- - [Документация RuStore API](https://help.rustore.ru/rustore/for_developers/work_with_RuStore_API)
422
+ - [RuStore API: публикация приложений](https://www.rustore.ru/help/work-with-rustore-api/api-upload-publication-app)
76
423
  - [Fastlane Plugins Guide](https://docs.fastlane.tools/plugins/create-plugin/)
@@ -0,0 +1,66 @@
1
+ require 'fastlane/action'
2
+ require 'fastlane_core/ui/ui'
3
+ require_relative '../helper/upload_to_ru_store_helper'
4
+ require_relative '../helper/rustore/options'
5
+
6
+ module Fastlane
7
+ module Actions
8
+ class RustoreArchiveVersionAction < Action
9
+ Options = Helper::Rustore::Options
10
+ Api = Helper::Rustore::Api
11
+
12
+ def self.authors
13
+ ['Kopylov Ivan']
14
+ end
15
+
16
+ def self.description
17
+ 'Archives a RuStore application version'
18
+ end
19
+
20
+ def self.details
21
+ <<~DETAILS
22
+ RuStore only accepts archiving for versions in #{Api::ARCHIVABLE_STATUSES.join(' or ')}.
23
+ The action checks the current status first and fails early with a clear message
24
+ instead of letting the API return an opaque error.
25
+ DETAILS
26
+ end
27
+
28
+ def self.category
29
+ :production
30
+ end
31
+
32
+ def self.available_options
33
+ Options.auth + [Options.version_id(optional: false), Options.timeout]
34
+ end
35
+
36
+ def self.example_code
37
+ [
38
+ <<~EXAMPLE
39
+ rustore_archive_version(
40
+ package_name: "com.example.app",
41
+ key_id: ENV["RUSTORE_KEY_ID"],
42
+ private_key: ENV["RUSTORE_PRIVATE_KEY"],
43
+ version_id: 12345
44
+ )
45
+ EXAMPLE
46
+ ]
47
+ end
48
+
49
+ def self.is_supported?(platform)
50
+ platform == :android
51
+ end
52
+
53
+ def self.run(params)
54
+ helper = Helper::UploadToRuStoreHelper
55
+ token = helper.fetch_token(key_id: params[:key_id], private_key: params[:private_key])
56
+
57
+ helper.archive_version(
58
+ token: token,
59
+ package_name: params[:package_name],
60
+ version_id: params[:version_id]
61
+ )
62
+ params[:version_id]
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,81 @@
1
+ require 'fastlane/action'
2
+ require 'fastlane_core/ui/ui'
3
+ require_relative '../helper/upload_to_ru_store_helper'
4
+ require_relative '../helper/rustore/options'
5
+
6
+ module Fastlane
7
+ module Actions
8
+ class RustorePublishSettingsAction < Action
9
+ Options = Helper::Rustore::Options
10
+ Api = Helper::Rustore::Api
11
+
12
+ def self.authors
13
+ ['Kopylov Ivan']
14
+ end
15
+
16
+ def self.description
17
+ 'Changes publication settings of an existing RuStore version'
18
+ end
19
+
20
+ def self.details
21
+ <<~DETAILS
22
+ Updates the publication type, the delayed publication moment or the staged
23
+ rollout percentage of a version that already exists. RuStore only allows the
24
+ rollout percentage to grow, and 100 means a full release.
25
+ DETAILS
26
+ end
27
+
28
+ def self.category
29
+ :production
30
+ end
31
+
32
+ def self.available_options
33
+ Options.auth + [
34
+ Options.version_id(optional: false),
35
+ Options.publish_type,
36
+ Options.publish_datetime,
37
+ Options.partial_value,
38
+ Options.timeout
39
+ ]
40
+ end
41
+
42
+ def self.example_code
43
+ [
44
+ <<~EXAMPLE
45
+ rustore_publish_settings(
46
+ package_name: "com.example.app",
47
+ key_id: ENV["RUSTORE_KEY_ID"],
48
+ private_key: ENV["RUSTORE_PRIVATE_KEY"],
49
+ version_id: 12345,
50
+ partial_value: 50
51
+ )
52
+ EXAMPLE
53
+ ]
54
+ end
55
+
56
+ def self.is_supported?(platform)
57
+ platform == :android
58
+ end
59
+
60
+ def self.run(params)
61
+ helper = Helper::UploadToRuStoreHelper
62
+ token = helper.fetch_token(key_id: params[:key_id], private_key: params[:private_key])
63
+
64
+ settings = {
65
+ publish_type: params[:publish_type],
66
+ publish_datetime: params[:publish_datetime],
67
+ partial_value: params[:partial_value]
68
+ }.compact
69
+ UI.user_error!('Не передано ни одной настройки публикации') if settings.empty?
70
+
71
+ helper.update_publish_settings(
72
+ token: token,
73
+ package_name: params[:package_name],
74
+ version_id: params[:version_id],
75
+ **settings
76
+ )
77
+ params[:version_id]
78
+ end
79
+ end
80
+ end
81
+ end