fix(release): public links in release notes and a cancelled status
The job reaches the Gitea API at its internal address, so html_url in the response pointed at http://10.10.1.129:3000 and every link in the notes was unreachable from outside. Build links from PUBLIC_URL (repo variable, defaults to https://gitea.hsrv.site). A cancelled Android build now shows "cancelled" instead of staying at "building". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
718125b719
commit
a239650075
4 files changed
+29
-4
No files matched your search
@@ -5,6 +5,8 @@
|
|||||||
#
|
#
|
||||||
# Секреты (Settings → Actions → Secrets):
|
# Секреты (Settings → Actions → Secrets):
|
||||||
# RELEASE_TOKEN (необязательно) — токен для релиза; без него — встроенный токен job (права на запись в репозиторий)
|
# RELEASE_TOKEN (необязательно) — токен для релиза; без него — встроенный токен job (права на запись в репозиторий)
|
||||||
|
# Переменные (Settings → Actions → Variables):
|
||||||
|
# PUBLIC_URL (необязательно) — внешний адрес Gitea для ссылок в тексте релиза; по умолчанию https://gitea.hsrv.site
|
||||||
# ANDROID_KEYSTORE_BASE64, ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_ALIAS, ANDROID_KEY_PASSWORD — подпись APK
|
# ANDROID_KEYSTORE_BASE64, ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_ALIAS, ANDROID_KEY_PASSWORD — подпись APK
|
||||||
name: release
|
name: release
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||||
|
PUBLIC_URL: ${{ vars.PUBLIC_URL || 'https://gitea.hsrv.site' }}
|
||||||
TAG: ${{ github.ref_name }}
|
TAG: ${{ github.ref_name }}
|
||||||
VERSION: ${{ steps.v.outputs.PIPU_VERSION }}
|
VERSION: ${{ steps.v.outputs.PIPU_VERSION }}
|
||||||
PRERELEASE: ${{ steps.v.outputs.PIPU_PRERELEASE }}
|
PRERELEASE: ${{ steps.v.outputs.PIPU_PRERELEASE }}
|
||||||
@@ -60,6 +63,7 @@ jobs:
|
|||||||
# для scripts/release.mjs
|
# для scripts/release.mjs
|
||||||
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||||
|
PUBLIC_URL: ${{ vars.PUBLIC_URL || 'https://gitea.hsrv.site' }}
|
||||||
TAG: ${{ github.ref_name }}
|
TAG: ${{ github.ref_name }}
|
||||||
VERSION: ${{ needs.release.outputs.version }}
|
VERSION: ${{ needs.release.outputs.version }}
|
||||||
VERSION_CODE: ${{ needs.release.outputs.version_code }}
|
VERSION_CODE: ${{ needs.release.outputs.version_code }}
|
||||||
@@ -141,3 +145,8 @@ jobs:
|
|||||||
- name: Статус в релизе — сборка не удалась
|
- name: Статус в релизе — сборка не удалась
|
||||||
if: failure()
|
if: failure()
|
||||||
run: node scripts/release.mjs android failure
|
run: node scripts/release.mjs android failure
|
||||||
|
|
||||||
|
# срабатывает при отмене из интерфейса; если раннер убит целиком, статус обновит следующий запуск
|
||||||
|
- name: Статус в релизе — сборка отменена
|
||||||
|
if: cancelled()
|
||||||
|
run: node scripts/release.mjs android cancelled
|
||||||
@@ -106,6 +106,10 @@ container:
|
|||||||
| `RELEASE_TOKEN` | Необязательно. Создание и редактирование релиза, загрузка ассетов (scope `write:repository`). Без него используется встроенный токен job (`GITHUB_TOKEN`), у которого есть права на запись в этот репозиторий |
|
| `RELEASE_TOKEN` | Необязательно. Создание и редактирование релиза, загрузка ассетов (scope `write:repository`). Без него используется встроенный токен job (`GITHUB_TOKEN`), у которого есть права на запись в этот репозиторий |
|
||||||
| `ANDROID_KEYSTORE_BASE64`, `ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEY_ALIAS`, `ANDROID_KEY_PASSWORD` | Подпись APK. **Ключ один на всю жизнь приложения** — хранить резервную копию вне Gitea. Без них финальный релиз падает; `-rc` собирается с отладочной подписью. |
|
| `ANDROID_KEYSTORE_BASE64`, `ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEY_ALIAS`, `ANDROID_KEY_PASSWORD` | Подпись APK. **Ключ один на всю жизнь приложения** — хранить резервную копию вне Gitea. Без них финальный релиз падает; `-rc` собирается с отладочной подписью. |
|
||||||
|
|
||||||
|
Переменная репозитория `PUBLIC_URL` (Settings → Actions → Variables) — внешний адрес Gitea для ссылок
|
||||||
|
в тексте релиза, по умолчанию `https://gitea.hsrv.site`. Нужна потому, что job обращается к API по внутреннему
|
||||||
|
адресу, и ссылки из ответа API ведут туда же.
|
||||||
|
|
||||||
## Деплой
|
## Деплой
|
||||||
|
|
||||||
Автодеплой не обязателен. На сервере в клоне репозитория: `deploy/update.sh [vX.Y.Z]` — checkout тега,
|
Автодеплой не обязателен. На сервере в клоне репозитория: `deploy/update.sh [vX.Y.Z]` — checkout тега,
|
||||||
|
|||||||
+15
-4
@@ -2,10 +2,12 @@
|
|||||||
// Релиз в Gitea (docs/ci-cd.md#релиз): сначала создаётся релиз с заметками, потом сборка Android
|
// Релиз в Gitea (docs/ci-cd.md#релиз): сначала создаётся релиз с заметками, потом сборка Android
|
||||||
// докладывает в него APK и переписывает блок статуса в тексте.
|
// докладывает в него APK и переписывает блок статуса в тексте.
|
||||||
// node scripts/release.mjs create — создать релиз по тегу (при перезапуске — обновить)
|
// node scripts/release.mjs create — создать релиз по тегу (при перезапуске — обновить)
|
||||||
// node scripts/release.mjs android building|failure — статус сборки APK в тексте релиза
|
// node scripts/release.mjs android building|failure|cancelled — статус сборки APK в тексте релиза
|
||||||
// node scripts/release.mjs android success <file.apk> — приложить APK и .sha256, статус «собран»
|
// node scripts/release.mjs android success <file.apk> — приложить APK и .sha256, статус «собран»
|
||||||
// Окружение: GITEA_API (<server>/api/v1/repos/<owner>/<repo>), GITEA_TOKEN, TAG, VERSION, VERSION_CODE,
|
// Окружение: GITEA_API (<server>/api/v1/repos/<owner>/<repo>), GITEA_TOKEN, TAG, VERSION, VERSION_CODE,
|
||||||
// PRERELEASE (true|false), GITHUB_RUN_ID; для success — SIGNING (release|debug).
|
// PRERELEASE (true|false), GITHUB_RUN_ID; для success — SIGNING (release|debug).
|
||||||
|
// PUBLIC_URL — внешний адрес Gitea для ссылок в тексте (job ходит в API по внутреннему адресу, и html_url
|
||||||
|
// из ответа указывает на него же).
|
||||||
|
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { createHash } from 'node:crypto';
|
import { createHash } from 'node:crypto';
|
||||||
@@ -57,6 +59,11 @@ export function renderAndroidStatus({ status, runUrl, versionCode, apk, signing
|
|||||||
? ['⚠️ Подписан отладочным ключом: не установится поверх версии с релизной подписью.']
|
? ['⚠️ Подписан отладочным ключом: не установится поверх версии с релизной подписью.']
|
||||||
: []),
|
: []),
|
||||||
]
|
]
|
||||||
|
: status === 'cancelled'
|
||||||
|
? [
|
||||||
|
`⏹ **Android:** сборка APK отменена — [лог сборки](${runUrl}).`,
|
||||||
|
'APK появится здесь после перезапуска сборки.',
|
||||||
|
]
|
||||||
: status === 'failure'
|
: status === 'failure'
|
||||||
? [
|
? [
|
||||||
`❌ **Android:** сборка APK не удалась — [лог сборки](${runUrl}).`,
|
`❌ **Android:** сборка APK не удалась — [лог сборки](${runUrl}).`,
|
||||||
@@ -182,9 +189,11 @@ function commitsBetween(prevTag, tag) {
|
|||||||
|
|
||||||
async function context() {
|
async function context() {
|
||||||
const repo = await api('GET', '');
|
const repo = await api('GET', '');
|
||||||
|
const publicUrl = process.env.PUBLIC_URL?.replace(/\/+$/, '');
|
||||||
|
const repoUrl = publicUrl ? `${publicUrl}/${repo.full_name}` : repo.html_url;
|
||||||
const tag = env('TAG');
|
const tag = env('TAG');
|
||||||
const release = await api('GET', `/releases/tags/${encodeURIComponent(tag)}`);
|
const release = await api('GET', `/releases/tags/${encodeURIComponent(tag)}`);
|
||||||
return { repoUrl: repo.html_url, tag, release, runUrl: `${repo.html_url}/actions/runs/${env('GITHUB_RUN_ID')}` };
|
return { repoUrl, tag, release, runUrl: `${repoUrl}/actions/runs/${env('GITHUB_RUN_ID')}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function create() {
|
async function create() {
|
||||||
@@ -242,8 +251,10 @@ async function android(status, apkPath) {
|
|||||||
async function main() {
|
async function main() {
|
||||||
const [cmd, ...args] = process.argv.slice(2);
|
const [cmd, ...args] = process.argv.slice(2);
|
||||||
if (cmd === 'create') return create();
|
if (cmd === 'create') return create();
|
||||||
if (cmd === 'android' && ['building', 'success', 'failure'].includes(args[0])) return android(args[0], args[1]);
|
if (cmd === 'android' && ['building', 'success', 'failure', 'cancelled'].includes(args[0])) {
|
||||||
throw new Error('использование: release.mjs create | android building|failure | android success <file.apk>');
|
return android(args[0], args[1]);
|
||||||
|
}
|
||||||
|
throw new Error('использование: release.mjs create | android building|failure|cancelled | android success <file.apk>');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ test('android status blocks', () => {
|
|||||||
assert.match(debug, /отладочным ключом/);
|
assert.match(debug, /отладочным ключом/);
|
||||||
|
|
||||||
assert.match(renderAndroidStatus({ status: 'failure', runUrl: 'https://run' }), /не удалась — \[лог сборки\]/);
|
assert.match(renderAndroidStatus({ status: 'failure', runUrl: 'https://run' }), /не удалась — \[лог сборки\]/);
|
||||||
|
assert.match(renderAndroidStatus({ status: 'cancelled', runUrl: 'https://run' }), /отменена — \[лог сборки\]/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('replaces only the status block and is idempotent', () => {
|
test('replaces only the status block and is idempotent', () => {
|
||||||
|
|||||||
Reference in new issue
Block a user