From 60264ff177fbbb6a74aa83fc40ea3a53ab1f1d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20Sonic?= Date: Mon, 10 Aug 2026 22:29:54 +0300 Subject: [PATCH] =?UTF-8?q?ci:=20=D1=81=D1=82=D1=80=D0=B0=D0=B6=20=D1=82?= =?UTF-8?q?=D0=B5=D0=B3=D0=B0=20=D0=BD=D0=B0=20HEAD=20=D0=B8=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=BD=D1=8B=D0=B9=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=80=D1=8F=D0=B4=D0=BE=D0=BA=20=D0=B2=D1=8B=D0=BF=D1=83?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=20(#41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Первый заход v2.0.0-beta.8 собрался на всех трёх платформах и упал на публикации с «Resource not accessible by integration». Сообщение про права уводит не туда: права были ровно те же, что у beta.7 (Contents: write), tauri-action тот же коммит, правил на теги нет. Настоящая причина — положение тега. Токен Actions создаёт релиз только на HEAD ветки по умолчанию. Тег поставили на chore(release), следом дописали коммит в main, и к моменту вызова API тег отстал на один коммит. Задача guard сверяет тег с HEAD и валится за секунды до сборок, вместо загадочного 403 через восемь минут. Она ловит тег на старом коммите, но не ловит гонку «запушили в main во время сборки» — то есть ровно тот случай, который и произошёл. Поэтому главная защита не в ней, а в порядке действий, записанном в docs/RELEASING.md: тег ставится последним, во время релиза в main не пушим. Логика стража прогнана на обеих ветках на реальных SHA этого репозитория. Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com> --- .github/workflows/release.yml | 33 ++++++++++++++++ docs/RELEASING.md | 74 +++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 docs/RELEASING.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cc0ac93..005c320 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,40 @@ permissions: contents: write jobs: + # Токен Actions умеет создавать релиз только на HEAD ветки по умолчанию. Если + # тег отстал хоть на один коммит, GitHub отвечает «Resource not accessible by + # integration» — сообщение про права, хотя права в порядке и дело в положении + # тега. Так утонул v2.0.0-beta.8: тег поставили, следом дописали коммит в main, + # и три сборки по восемь минут закончились загадочным 403. + # + # Проверка занимает секунды и идёт до сборок. Но она НЕ закрывает гонку: если + # запушить в main уже после её прохождения, публикация всё равно упадёт — как и + # случилось с beta.8. Единственная настоящая защита — порядок действий: тег + # ставится последним, и пока идёт релиз, в main не пушим. + guard: + name: Тег должен стоять на HEAD + runs-on: ubuntu-latest + steps: + - name: Сверить тег с веткой по умолчанию + env: + GH_TOKEN: ${{ github.token }} + run: | + branch=$(gh api "repos/$GITHUB_REPOSITORY" -q .default_branch) + head=$(gh api "repos/$GITHUB_REPOSITORY/commits/$branch" -q .sha) + if [ "$head" = "$GITHUB_SHA" ]; then + echo "$GITHUB_REF_NAME и $branch указывают на $head — собираем." + exit 0 + fi + echo "::error::$GITHUB_REF_NAME стоит на $GITHUB_SHA, а $branch — на $head. Публикация упадёт с 403, потому что токен Actions создаёт релиз только на HEAD ветки по умолчанию." + echo "Переставьте тег и запушьте заново:" + echo " git tag -d $GITHUB_REF_NAME" + echo " git push origin :refs/tags/$GITHUB_REF_NAME" + echo " git tag -a $GITHUB_REF_NAME $head -m 'TGLock ...'" + echo " git push origin $GITHUB_REF_NAME" + exit 1 + publish: + needs: guard name: Build ${{ matrix.platform }} strategy: fail-fast: false diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..23f341e --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,74 @@ +# Как выпускать релиз + +## Порядок важен + +Токен GitHub Actions создаёт релиз **только на HEAD ветки по умолчанию**. Если тег +отстанет от `main` хотя бы на один коммит, публикация упадёт с ошибкой +`Resource not accessible by integration` — сообщение про права, хотя права в +порядке и дело в положении тега. + +Отсюда единственное жёсткое правило: **тег ставится последним, и пока идёт +релиз, в `main` не пушим.** + +## Шаги + +1. Влить в `main` всё, что должно попасть в релиз, и дождаться зелёного CI. +2. Поднять версию **в двух местах** — `Cargo.toml` и `tauri.conf.json`. Они + должны совпадать: имена файлов бандла берутся из `tauri.conf.json`. +3. Закоммитить подъём версии и запушить в `main`. +4. Убедиться, что больше ничего не уедет: `git ls-remote origin refs/heads/main` + должен совпасть с локальным `git rev-parse HEAD`. +5. Поставить аннотированный тег на этот же коммит и запушить его: + + ```bash + git tag -a v2.0.0-beta.N -m "TGLock 2.0.0-beta.N" + git push origin v2.0.0-beta.N + ``` + +6. **Ничего не пушить в `main`, пока сборка не закончится.** Правки README, + документации, чего угодно — после публикации релиза. + +## Проверить, что выпустили + +Зелёный workflow — это ещё не доказательство. В бетах 2 и 3 сборка была зелёной, +а в приложение попадал headless-бинарь вместо графического. Поэтому проверяем +содержимое, а не имя файла: + +```bash +gh release download vX.Y.Z --repo by-sonic/tglock -p 'TGLock_universal.app.tar.gz' -D /tmp/check +tar -xzf /tmp/check/TGLock_universal.app.tar.gz -C /tmp/check +python scripts/verify_bundle_binary.py /tmp/check/TGLock.app/Contents/MacOS/tglock +``` + +Скрипт ищет внутри бинаря маркеры GUI (`ipc.localhost`, `wry`) и маркеры CLI +(`allow-direct`, `secret-file`) и ругается, если в бандле оказался не тот. + +## Если публикация всё-таки упала с `Resource not accessible by integration` + +Значит, тег разошёлся с `main`. Сверьте: + +```bash +git ls-remote origin refs/heads/main 'refs/tags/vX.Y.Z^{}' +``` + +Если SHA разные — переставьте тег на HEAD и запушьте заново: + +```bash +git tag -d vX.Y.Z +git push origin :refs/tags/vX.Y.Z +git tag -a vX.Y.Z -m "TGLock X.Y.Z" +git push origin vX.Y.Z +``` + +Перезапускать упавший workflow бесполезно: он возьмёт тот же отставший тег и +упадёт снова. + +## Что защищает автоматически + +Первым шагом релиза идёт задача `guard`: она сверяет тег с HEAD ветки по +умолчанию и валится за секунды, не запуская сборки. Она ловит тег, поставленный +на старый коммит. + +Она **не** ловит гонку: если запушить в `main` уже после её прохождения, но до +конца сборки, публикация упадёт. Ровно так утонул первый заход v2.0.0-beta.8. +От этого защищает только правило из первого раздела.