diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index e1b8066..f124920 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -47,4 +47,61 @@ jobs: run: $HOME/.local/bin/uv sync --group dev - name: Run tests - run: PATH="$HOME/.local/bin:$PATH" make test \ No newline at end of file + run: PATH="$HOME/.local/bin:$PATH" make test + + publish: + name: Publish to Gitea Packages + runs-on: ubuntu-latest + needs: [quality, tests] + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install uv + run: curl -LsSf https://astral.sh/uv/install.sh | sh + + - name: Install Python from .python-version + run: $HOME/.local/bin/uv python install + + - name: Sync development dependencies + run: $HOME/.local/bin/uv sync --group dev + + - name: Read package version from pyproject.toml + id: package_meta + run: | + $HOME/.local/bin/uv run python - <<'PY' >> "$GITHUB_OUTPUT" + import tomllib + from pathlib import Path + + project = tomllib.loads(Path('pyproject.toml').read_text(encoding='utf-8'))['project'] + print(f"name={project['name']}") + print(f"version={project['version']}") + PY + + - name: Show package version + run: echo "Publishing ${{ steps.package_meta.outputs.name }} version ${{ steps.package_meta.outputs.version }}" + + - name: Validate package registry configuration + env: + GITEA_PYPI_REPOSITORY_URL: ${{ secrets.GITEA_PYPI_REPOSITORY_URL }} + GITEA_PACKAGE_USERNAME: ${{ secrets.GITEA_PACKAGE_USERNAME }} + GITEA_PACKAGE_TOKEN: ${{ secrets.GITEA_PACKAGE_TOKEN }} + run: | + test -n "$GITEA_PYPI_REPOSITORY_URL" || (echo "GITEA_PYPI_REPOSITORY_URL secret is required" && exit 1) + test -n "$GITEA_PACKAGE_USERNAME" || (echo "GITEA_PACKAGE_USERNAME secret is required" && exit 1) + test -n "$GITEA_PACKAGE_TOKEN" || (echo "GITEA_PACKAGE_TOKEN secret is required" && exit 1) + + - name: Build distribution + run: PATH="$HOME/.local/bin:$PATH" make build + + - name: Check built artifacts + run: PATH="$HOME/.local/bin:$PATH" uv run --with twine twine check dist/* + + - name: Publish to Gitea PyPI registry + env: + TWINE_REPOSITORY_URL: ${{ secrets.GITEA_PYPI_REPOSITORY_URL }} + TWINE_USERNAME: ${{ secrets.GITEA_PACKAGE_USERNAME }} + TWINE_PASSWORD: ${{ secrets.GITEA_PACKAGE_TOKEN }} + run: PATH="$HOME/.local/bin:$PATH" uv run --with twine twine upload --skip-existing dist/* \ No newline at end of file diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml deleted file mode 100644 index ff25b8d..0000000 --- a/.gitea/workflows/release.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Release - -on: - push: - tags: - - "v*" - -jobs: - quality: - name: Quality Checks - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install uv - run: curl -LsSf https://astral.sh/uv/install.sh | sh - - - name: Install Python from .python-version - run: $HOME/.local/bin/uv python install - - - name: Sync development dependencies - run: $HOME/.local/bin/uv sync --group dev - - - name: Run quality checks - run: PATH="$HOME/.local/bin:$PATH" make ci-check - - tests: - name: Test Suite - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install uv - run: curl -LsSf https://astral.sh/uv/install.sh | sh - - - name: Install Python from .python-version - run: $HOME/.local/bin/uv python install - - - name: Sync development dependencies - run: $HOME/.local/bin/uv sync --group dev - - - name: Run tests - run: PATH="$HOME/.local/bin:$PATH" make test - - publish: - name: Publish to Gitea Packages - runs-on: ubuntu-latest - needs: [quality, tests] - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install uv - run: curl -LsSf https://astral.sh/uv/install.sh | sh - - - name: Install Python from .python-version - run: $HOME/.local/bin/uv python install - - - name: Sync development dependencies - run: $HOME/.local/bin/uv sync --group dev - - - name: Validate package registry configuration - env: - PYPI_REPOSITORY_URL: ${{ secrets.PYPI_REPOSITORY_URL }} - PACKAGE_USERNAME: ${{ secrets.PACKAGE_USERNAME }} - PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }} - run: | - test -n "$PYPI_REPOSITORY_URL" || (echo "PYPI_REPOSITORY_URL secret is required" && exit 1) - test -n "$PACKAGE_USERNAME" || (echo "PACKAGE_USERNAME secret is required" && exit 1) - test -n "$PACKAGE_TOKEN" || (echo "PACKAGE_TOKEN secret is required" && exit 1) - - - name: Build distribution - run: PATH="$HOME/.local/bin:$PATH" make build - - - name: Check built artifacts - run: PATH="$HOME/.local/bin:$PATH" uv run --with twine twine check dist/* - - - name: Publish to Gitea PyPI registry - env: - TWINE_REPOSITORY_URL: ${{ secrets.PYPI_REPOSITORY_URL }} - TWINE_USERNAME: ${{ secrets.PACKAGE_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PACKAGE_TOKEN }} - run: PATH="$HOME/.local/bin:$PATH" uv run --with twine twine upload dist/* \ No newline at end of file diff --git a/Makefile b/Makefile index 4e166a0..b0d4b3c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ UV := uv -.PHONY: format lint typecheck test ci-check build publish-gitea clean-branches +.PHONY: format lint typecheck test ci-check build package-version publish-gitea clean-branches format: $(UV) run ruff format . @@ -23,16 +23,20 @@ ci-check: build: $(UV) build +package-version: + $(UV) run python -c "import tomllib; from pathlib import Path; print(tomllib.loads(Path('pyproject.toml').read_text(encoding='utf-8'))['project']['version'])" + publish-gitea: @test -n "$$GITEA_PYPI_REPOSITORY_URL" || (echo "GITEA_PYPI_REPOSITORY_URL is required" && exit 1) @test -n "$$GITEA_PACKAGE_USERNAME" || (echo "GITEA_PACKAGE_USERNAME is required" && exit 1) @test -n "$$GITEA_PACKAGE_TOKEN" || (echo "GITEA_PACKAGE_TOKEN is required" && exit 1) + @echo "Publishing version $$($(MAKE) --no-print-directory package-version)" $(UV) build $(UV) run --with twine twine check dist/* TWINE_REPOSITORY_URL="$$GITEA_PYPI_REPOSITORY_URL" \ TWINE_USERNAME="$$GITEA_PACKAGE_USERNAME" \ TWINE_PASSWORD="$$GITEA_PACKAGE_TOKEN" \ - $(UV) run --with twine twine upload dist/* + $(UV) run --with twine twine upload --skip-existing dist/* clean-branches: ifeq ($(OS),Windows_NT) diff --git a/README.md b/README.md index 5d667bc..39536e5 100644 --- a/README.md +++ b/README.md @@ -84,18 +84,20 @@ make build В репозитории добавлены Gitea Actions workflow: -- .gitea/workflows/ci.yml: lint, mypy, deptry, pytest -- .gitea/workflows/release.yml: повторный прогон quality/tests на теге и публикация в Gitea Packages только после их успеха +- .gitea/workflows/ci.yml: lint, mypy, deptry, pytest и автоматическая публикация в Gitea Packages после успешного CI на push в main Публикация настроена в Gitea PyPI registry по документации Gitea Packages. +Версия пакета берётся из секции [project] -> version в pyproject.toml. -Нужные secrets для release workflow: +Нужные secrets для CI workflow: - GITEA_PYPI_REPOSITORY_URL: полный endpoint вида https://gitea.example.com/api/packages//pypi - GITEA_PACKAGE_USERNAME: пользователь Gitea - GITEA_PACKAGE_TOKEN: personal access token с правом package write -Публикация идёт только на push тега вида v* и только после успешных lint/typecheck/test jobs. +Публикация идёт автоматически на push в main после успешных lint/typecheck/test jobs. +Если версия пакета уже существует в registry, upload будет пропущен через --skip-existing. +Если версия в pyproject.toml увеличена, в Gitea Packages появится новая версия пакета, а старые версии останутся доступными. Локально тот же сценарий можно выполнить так: @@ -103,5 +105,6 @@ make build export GITEA_PYPI_REPOSITORY_URL="https://gitea.example.com/api/packages//pypi" export GITEA_PACKAGE_USERNAME="" export GITEA_PACKAGE_TOKEN="" +make package-version make publish-gitea ```