From 038d033546ba9d764799ceed7158be677801a1ce Mon Sep 17 00:00:00 2001 From: Sergey Ostroukhov Date: Wed, 26 Aug 2026 13:48:30 +0300 Subject: [PATCH] =?UTF-8?q?feat(ci):=20=D1=80=D0=B5=D0=BB=D0=B8=D0=B7=20?= =?UTF-8?q?=D0=B2=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D1=8F=D0=B5=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BE=20=D1=82=D0=B5=D0=B3=D1=83,=20=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B5=20=D0=BD=D0=B0=20=D0=BA=D0=B0=D0=B6=D0=B4=D1=8B?= =?UTF-8?q?=D0=B9=20push=20=D0=B2=20main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Публикация в PyPI и в Docker Hub запускалась на каждый push в main, тогда как версия пакета меняется только в релизных коммитах. Любой обычный коммит или merge PR пересобирал уже выложенный дистрибутив и получал 400 File already exists ('mdast_cli-2026.8.6-py3-none-any.whl' ...) после чего workflow падал. Образ latest на Docker Hub при этом перезаписывался на каждый коммит, релизный он или нет. Оба workflow переведены на триггер по тегу: on: push: tags: - '*' Публикация становится осознанным действием, а не побочным эффектом мержа. Тесты обычных коммитов и PR не затронуты: их гоняет tests.yml, у которого триггеры прежние. Версия образа Docker больше не прописана в файле руками, а берётся из тега (github.ref_name). Раньше её приходилось править в двух местах при каждом релизе, и рассинхрон с setup.py ничем не отслеживался. Добавлена проверка, что тег совпадает с версией в setup.py. Иначе под тегом уедет дистрибутив с другим номером, а откатить это на PyPI нельзя - файлы там неизменяемы. Проверка выполняется до сборки и публикации. Порядок релиза становится таким: 1. поднять версию в setup.py, влить в main; 2. поставить тег с тем же номером и запушить его. --- .github/workflows/docker-hub-publish.yml | 34 +++++++++++++++----- .github/workflows/python-publish.yml | 41 ++++++++++++++---------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/.github/workflows/docker-hub-publish.yml b/.github/workflows/docker-hub-publish.yml index 6c4a4ad..ce4bf1f 100644 --- a/.github/workflows/docker-hub-publish.yml +++ b/.github/workflows/docker-hub-publish.yml @@ -1,9 +1,11 @@ name: Publish mdast_cli image to Docker Hub +# Публикация выполняется только по тегу, как и выкладка на PyPI: иначе каждый +# коммит в main перезаписывал образ latest и заново публиковал тот же номер. on: push: - branches: - - main + tags: + - '*' jobs: tests: @@ -25,6 +27,22 @@ jobs: steps: - uses: actions/checkout@v7 + # Версия образа берётся из тега, а не прописывается в файле руками: + # раньше её приходилось править в двух местах при каждом релизе. + - name: Check tag matches package version + run: | + set -eu + pkg_version="$(sed -n "s/.*version=['\"]\([^'\"]*\).*/\1/p" setup.py | head -1)" + if [ -z "$pkg_version" ]; then + echo "::error::Не удалось определить версию в setup.py" + exit 1 + fi + if [ "$GITHUB_REF_NAME" != "$pkg_version" ]; then + echo "::error::Тег $GITHUB_REF_NAME не совпадает с версией в setup.py ($pkg_version)" + exit 1 + fi + echo "Тег и версия совпадают: $pkg_version" + - name: docker login env: DOCKER_USER: ${{secrets.DOCKER_USER}} @@ -33,13 +51,13 @@ jobs: docker login -u $DOCKER_USER -p $DOCKER_PASSWORD - name: Build the Docker image + run: >- + docker build . --file Dockerfile + -t mobilesecurity/mdast_cli:${{ github.ref_name }} + -t mobilesecurity/mdast_cli:latest - run: docker build . --file Dockerfile -t mobilesecurity/mdast_cli:2026.8.6 -t mobilesecurity/mdast_cli:latest - + - name: Docker Hub push tagged image + run: docker push mobilesecurity/mdast_cli:${{ github.ref_name }} - name: Docker Hub push latest image run: docker push mobilesecurity/mdast_cli:latest - - - name: Docker Hub push tagged image - - run: docker push mobilesecurity/mdast_cli:2026.8.6 diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 5f14394..63640e2 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -1,9 +1,12 @@ name: Publish mdast_cli 🐍 distributions to PyPI -on: - push: - branches: - - main +# Публикация выполняется только по тегу. Раньше workflow запускался на каждый +# push в main, из-за чего любой нерелизный коммит пересобирал уже выложенную +# версию и падал с "400 File already exists". +on: + push: + tags: + - '*' jobs: tests: @@ -29,17 +32,22 @@ jobs: with: python-version: '3.12' -# - name: Linter flake8 -# uses: py-actions/flake8@v2.2.1 -# with: -# exclude: "build,dist,check_mdast_cli,googleplay_pb2.py" -# max-line-length: "120" -# -# - name: Linter isort -# uses: isort/isort-action@v1.1.0 -# with: -# configuration: "--skip ./docker --skip ./venv --skip ./check_mdast_cli --line-length 120 --check-only" -# requirements-files: "requirements.txt" + # Тег задаёт имя релиза, а версию пакета определяет setup.py. Разъехавшись, + # они выложат под тегом дистрибутив с другим номером, и откатить это на PyPI + # уже нельзя - файлы неизменяемы. + - name: Check tag matches package version + run: | + set -eu + pkg_version="$(sed -n "s/.*version=['\"]\([^'\"]*\).*/\1/p" setup.py | head -1)" + if [ -z "$pkg_version" ]; then + echo "::error::Не удалось определить версию в setup.py" + exit 1 + fi + if [ "$GITHUB_REF_NAME" != "$pkg_version" ]; then + echo "::error::Тег $GITHUB_REF_NAME не совпадает с версией в setup.py ($pkg_version)" + exit 1 + fi + echo "Тег и версия совпадают: $pkg_version" - name: Install pypa/build run: >- @@ -65,8 +73,7 @@ jobs: repository-url: https://test.pypi.org/legacy/ - name: Publish distribution 📦 to PyPI - if: github.event_name == 'push' uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ - password: ${{ secrets.PYPI_API_CLI_TOKEN }} \ No newline at end of file + password: ${{ secrets.PYPI_API_CLI_TOKEN }}