Skip to content

Подпись release APK ключом из GitHub Secrets - #7

Merged
maximkr merged 1 commit into
mainfrom
feat/release-signing
Aug 12, 2026
Merged

Подпись release APK ключом из GitHub Secrets#7
maximkr merged 1 commit into
mainfrom
feat/release-signing

Conversation

@maximkr

@maximkr maximkr commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Отвечает на вопрос «можно ли прикрутить подпись от RuStore к GitHub приватно». Да, через GitHub Actions Secrets — но с оговоркой, она в конце.

Что меняется

app/build.gradle.ktssigningConfig читается из переменных окружения KEYSTORE_PATH, KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD. Ни ключ, ни пароли в репозиторий не попадают.

Если KEYSTORE_PATH не задан, signingConfig не создаётся вовсе — локальная сборка и сборка в форках продолжают работать без доступа к ключу и просто выдают неподписанный APK.

.github/workflows/release.yml — ключ приезжает из секрета KEYSTORE_BASE64, раскладывается во временный каталог раннера и удаляется шагом с if: always(), то есть даже если сборка упала.

Отдельный шаг после сборки печатает отпечаток сертификата через apksigner verify --print-certs. Сверьте SHA-256 с тем, что показывает RuStore, — так вы убедитесь, что ключ тот самый и обновление встанет поверх магазинной версии.

Workflow не ломается, если секретов нет. Тогда, как и раньше, собирается debug APK с предупреждением о несовпадении подписи. Так что этот PR можно мержить до того, как вы добавите секреты.

.gitignore*.jks, *.keystore, keystore.properties, signing.properties.

Что сделать вам

Четыре секрета в Settings → Secrets and variables → Actions. Не присылайте мне пароли и не вставляйте их в чат — задайте их сами:

# из каталога, где лежит keystore
base64 -w0 release.jks > keystore.b64
gh secret set KEYSTORE_BASE64   --repo maximkr/rfid-manager < keystore.b64
gh secret set KEYSTORE_PASSWORD --repo maximkr/rfid-manager
gh secret set KEY_ALIAS         --repo maximkr/rfid-manager
gh secret set KEY_PASSWORD      --repo maximkr/rfid-manager
rm keystore.b64

Три последние команды спросят значение интерактивно, не отображая его. На macOS вместо base64 -w0 нужно base64 -i release.jks -o keystore.b64.

Оговорка, которую надо понимать

Секреты GitHub шифруются и маскируются в логах, но любой, у кого есть право записи в репозиторий, может их извлечь — достаточно добавить workflow, который отправит их куда угодно. Маскирование ловит прямой вывод, но не, скажем, base64 от секрета.

Для ключа, которым подписан RuStore-релиз, цена утечки высокая: чужая сборка сможет установиться как обновление вашего приложения.

Что снижает риск, по убыванию пользы:

  1. Environment с обязательным подтверждением. Создайте environment release, привяжите секреты к нему, включите required reviewers — тогда job не стартует, пока вы вручную не подтвердите запуск. Скажите, если нужно, — добавлю environment: release в workflow.
  2. PR из форков секретов не получают — это поведение GitHub по умолчанию, и оно уже работает. Важно не добавлять в репозиторий workflow с триггером pull_request_target.
  3. Отдельный ключ для GitHub. Если для RuStore допустима загрузка через ключ загрузки, а не ключ подписи приложения, — держите в GitHub только его.
  4. Бэкап keystore вне GitHub. Потеряв его, вы не сможете выпускать обновления вообще нигде.

Если такой риск не устраивает — рабочая альтернатива: подписывать локально и прикладывать APK к релизу руками, а CI оставить только для тестов и сборки. Скажите, и я переделаю.

🤖 Generated with Claude Code

app/build.gradle.kts: signingConfig читается из переменных окружения
KEYSTORE_PATH / KEYSTORE_PASSWORD / KEY_ALIAS / KEY_PASSWORD. Ключ и
пароли в репозиторий не попадают. Если KEYSTORE_PATH не задан,
signingConfig не создается вовсе, поэтому локальная сборка и сборка
в форках продолжают работать без доступа к ключу.

release.yml: ключ приезжает из секрета KEYSTORE_BASE64, раскладывается
во временный каталог раннера и удаляется шагом с if: always().
Отдельный шаг печатает отпечаток сертификата подписи, чтобы можно было
сверить SHA-256 с тем, что показывает RuStore.

Workflow не падает, если секреты не заданы: тогда выпускается debug APK
с прежним предупреждением о несовпадении подписи. Так релизы продолжат
работать и до того, как секреты будут добавлены.

.gitignore: *.jks, *.keystore, keystore.properties, signing.properties.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@maximkr
maximkr merged commit 6a76dbf into main Aug 12, 2026
1 check passed
@maximkr
maximkr deleted the feat/release-signing branch August 12, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant