ci: разрешить равную версию в release.yml только для ретрая того же коммита

Гейт «версия не должна уменьшаться» отвергал и равную версию безусловно, что
делало невозможным повторный прогон на том же теге (Task 10 Step 4 плана).
Теперь сравнение version+sourceSha читается из plugin/server.lock.json ветки
dist: равная версия проходит дальше только если sourceSha из dist совпадает с
текущим github.sha (ретрай уже опубликованного релиза — ниже по пайплайну он
и так идемпотентен: upsert релиза/ассета, повторная сверка sha256, force-push
dist тем же содержимым). Другой sourceSha при той же версии — попытка
подменить уже опубликованный релиз другим кодом — отвергается с явным
сообщением, какой коммит уже занимает эту версию. Меньшая версия отвергается
как и раньше.
This commit is contained in:
2026-07-31 02:55:39 +03:00
parent e2fe197fbb
commit 55045184e2
+35 -11
View File
@@ -43,26 +43,50 @@ jobs:
- name: версия не должна уменьшаться
run: |
NEW="${{ github.ref_name }}"; NEW="${NEW#v}"
CURRENT_SHA="${{ github.sha }}"
# Сверяем по plugin/server.lock.json (не plugin.json): в нём вместе с версией
# лежит и sourceSha, а он нужен ниже, чтобы отличить ретрай того же релиза от
# попытки переопубликовать ту же версию другим коммитом.
#
# `git show origin/dist:...` может не найти ни ветку, ни файл (первый релиз —
# dist ещё не существует). Не полагаемся на `cmd || echo fallback` вокруг пайпа
# с jq: jq на пустом stdin ничего не печатает, но выходит с кодом 0, поэтому
# такой fallback никогда бы не сработал — здесь fallback навешан на сам git show,
# jq вызывается только когда файл реально прочитан.
# jq вызывается только когда файл реально прочитан. Без dist сравнивать sourceSha
# не с чем — OLD_SOURCE_SHA остаётся пустым, и равенство версий ниже недостижимо
# (OLD_VERSION="0.0.0" всегда меньше валидного тега vX.Y.Z, кроме вырожденного
# v0.0.0, который проверка формата тега не отсекает, но это не наш случай).
git fetch origin dist 2>/dev/null || true
OLD="0.0.0"
if git show origin/dist:plugin/.claude-plugin/plugin.json > /tmp/dist-plugin.json 2>/dev/null; then
OLD=$(jq -r '.version // "0.0.0"' /tmp/dist-plugin.json)
OLD_VERSION="0.0.0"
OLD_SOURCE_SHA=""
if git show origin/dist:plugin/server.lock.json > /tmp/dist-lock.json 2>/dev/null; then
OLD_VERSION=$(jq -r '.version // "0.0.0"' /tmp/dist-lock.json)
OLD_SOURCE_SHA=$(jq -r '.sourceSha // ""' /tmp/dist-lock.json)
fi
SMALLEST=$(printf '%s\n%s\n' "$OLD" "$NEW" | sort -V | head -1)
# Отвергаем и меньшую, и равную версию: SMALLEST должен быть строго OLD
# (т.е. OLD < NEW), а не совпадать с NEW.
if [ "$SMALLEST" != "$OLD" ] || [ "$OLD" = "$NEW" ]; then
echo "версия $NEW не больше опубликованной $OLD"
exit 1
if [ "$OLD_VERSION" = "$NEW" ]; then
# Равная версия допускается ровно в одном случае: тег указывает на тот же
# коммит, из которого эта версия уже была опубликована в dist — это ретрай
# (сбойнул шаг после публикации ассета, тег пересоздали на том же SHA и т.п.),
# и весь пайплайн ниже идемпотентен сам по себе (upsert релиза/ассета, повторная
# сверка sha256, force-push dist тем же содержимым). Если sourceSha другой — под
# тем же номером версии протаскивают другой код, это не ретрай, а подмена уже
# опубликованного релиза; такое отвергаем, а не публикуем молча поверх старого.
if [ -n "$OLD_SOURCE_SHA" ] && [ "$OLD_SOURCE_SHA" = "$CURRENT_SHA" ]; then
echo "ok: повтор релиза $NEW с того же коммита $CURRENT_SHA"
else
echo "версия $NEW уже опубликована из коммита ${OLD_SOURCE_SHA:-<неизвестно>} — поднимите версию"
exit 1
fi
else
SMALLEST=$(printf '%s\n%s\n' "$OLD_VERSION" "$NEW" | sort -V | head -1)
if [ "$SMALLEST" != "$OLD_VERSION" ]; then
echo "версия $NEW не больше опубликованной $OLD_VERSION"
exit 1
fi
echo "ok: $NEW > $OLD_VERSION"
fi
echo "ok: $NEW > $OLD"
- name: publish
run: |