From d6c6f8aa6a47a7e807643f8c7805704aab38a7c7 Mon Sep 17 00:00:00 2001 From: Yurii Puchkov Date: Tue, 26 Dec 2023 21:07:51 -0700 Subject: [PATCH 1/3] feat: Add PIP package --- .gitignore | 2 ++ Dockerfile | 27 ++++++++++----- README.md | 30 ++++++++++++++++ pyproject.toml | 34 +++++++++++++++++++ requirements-build.txt | 2 ++ src/pygitver | 7 ---- src/{ => pygitver}/__init__.py | 0 src/{ => pygitver}/changelogs_mngr.py | 11 +++--- src/{ => pygitver}/git.py | 0 src/{ => pygitver}/pygitver.py | 8 ++--- .../templates/changelog-common.tmpl | 0 src/{ => pygitver}/templates/changelog.tmpl | 0 tests/__init__.py | 0 tests/test_changelogs_mngr.py | 18 ++++++++-- tests/test_git.py | 4 +-- 15 files changed, 114 insertions(+), 29 deletions(-) create mode 100644 pyproject.toml create mode 100644 requirements-build.txt delete mode 100755 src/pygitver rename src/{ => pygitver}/__init__.py (100%) rename src/{ => pygitver}/changelogs_mngr.py (88%) rename src/{ => pygitver}/git.py (100%) rename src/{ => pygitver}/pygitver.py (95%) rename src/{ => pygitver}/templates/changelog-common.tmpl (100%) rename src/{ => pygitver}/templates/changelog.tmpl (100%) delete mode 100644 tests/__init__.py diff --git a/.gitignore b/.gitignore index 45f23eb..78b41b3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ .coverage */__pycache__/* venv +dist +*.egg-info diff --git a/Dockerfile b/Dockerfile index ad82c71..83e8932 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,36 @@ +################################################################ FROM python:3.12-alpine as build # Make sure we use the virtualenv: RUN python -m venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" -# Install pip requirements -COPY ./requirements.txt /pygitver/requirements.txt -RUN pip install --no-cache-dir -U pip \ - && pip install --no-cache-dir -r /pygitver/requirements.txt +WORKDIR /build-pkg +# Install pip requirements +COPY . /build-pkg + +RUN apk add --no-cache git \ + && pip install --no-cache-dir -U pip \ + && pip install --no-cache-dir -r requirements.txt \ + && pip install --no-cache-dir -r requirements-build.txt \ + && rm -rf dist \ + && python -m build \ + && pip install $(ls /build-pkg/dist/pygitver-*.tar.gz) \ + && pip uninstall -r requirements-build.txt -y + +################################################################ FROM python:3.12-alpine ENV PYGITVER_TEMPLATE_CHANGELOG="/pygitver/templates/changelog.tmpl" +ENV PYGITVER_TEMPLATE_CHANGELOG_COMMON="/pygitver/templates/changelog-common.tmpl" ENV PYGITVER_ROOT="/pygitver" # Install dependencies -RUN apk add --no-cache git openssh \ - && ln -s /pygitver/pygitver /usr/local/bin/pygitver +RUN apk add --no-cache git openssh # Copy application code and -COPY ./src /pygitver +COPY ./src/pygitver/templates /pygitver/templates COPY ./scripts /pygitver/scripts COPY --from=build /opt/venv /opt/venv @@ -28,4 +39,4 @@ WORKDIR /app # Make sure we use the virtualenv: ENV PATH="/opt/venv/bin:$PATH" -ENTRYPOINT ["python", "/pygitver/pygitver.py"] +ENTRYPOINT ["pygitver"] diff --git a/README.md b/README.md index eef7f64..7adfaa9 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,24 @@ Features: # Examples +You may use it as a Python package or a Docker container. + +## PIP Package + +```shell +$ git tag -l +v0.0.1 +v0.0.2 +$ pygitver --curr-ver +v0.0.2 +$ pygitver --next-ver +v0.0.3 + +``` + + +## Docker Container + ```shell $ git tag -l v0.0.1 @@ -235,3 +253,15 @@ or ```shell coverage run -m pytest -- ./tests/test_git.py ``` + +#### Build pip package + +Linux +```shell +python3 -m build +``` + +For `Debian` based OS: +```shell +DEB_PYTHON_INSTALL_LAYOUT=deb_system python3 -m build +``` diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..bec520b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,34 @@ +[project] +name = "pygitver" +version = "0.0.2" +authors = [ + { name="Yurii Puchkov", email="panpuchkov@gmail.com" }, +] +license = {text = "GNU General Public License v3 (GPLv3)"} +description = "Manages Git Tag versions and generates ChangeLog" +keywords = ["git tag", "version", "versions", "conventional commit", "semver", "changelog", "changelogs"] +readme = "README.md" +requires-python = ">=3.8" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Natural Language :: English", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Operating System :: OS Independent", +] +dependencies = [ + "Jinja2==3.1.2" +] + + +[build-system] +requires = ["setuptools>=63", "wheel"] +build-backend = "setuptools.build_meta" + + +[project.scripts] +pygitver = "pygitver.pygitver:main" + +[metadata] +url = "https://github.com/panpuchkov/pygitver" diff --git a/requirements-build.txt b/requirements-build.txt new file mode 100644 index 0000000..89f5536 --- /dev/null +++ b/requirements-build.txt @@ -0,0 +1,2 @@ +build~=1.0 +setuptools~=69.0 diff --git a/src/pygitver b/src/pygitver deleted file mode 100755 index 9dd1def..0000000 --- a/src/pygitver +++ /dev/null @@ -1,7 +0,0 @@ -#/bin/sh - -# removes issue with git dubious ownership in a docker container -git config --global --add safe.directory $(pwd) - -# run pygitver with parameters -python /pygitver/pygitver.py $@ diff --git a/src/__init__.py b/src/pygitver/__init__.py similarity index 100% rename from src/__init__.py rename to src/pygitver/__init__.py diff --git a/src/changelogs_mngr.py b/src/pygitver/changelogs_mngr.py similarity index 88% rename from src/changelogs_mngr.py rename to src/pygitver/changelogs_mngr.py index d482d4c..dcc2f48 100644 --- a/src/changelogs_mngr.py +++ b/src/pygitver/changelogs_mngr.py @@ -1,6 +1,6 @@ import os import json -from git import Git, GitError +from pygitver.git import Git, GitError from jinja2 import Environment, FileSystemLoader, TemplateNotFound @@ -49,9 +49,12 @@ def read_files(self, path: str, file_ext: str = "json"): self._changelogs["version"] = None return self._changelogs - def changelog_generate( - self, template_name: str = "templates/changelog-common.tmpl" - ) -> str: + def generate(self, template_name: str = "") -> str: + if not template_name: + template_name = os.getenv( + "PYGITVER_TEMPLATE_CHANGELOG_COMMON", + "pygitver/templates/changelog-common.tmpl", + ) try: env = Environment(loader=FileSystemLoader(os.path.dirname(template_name))) template = env.get_template(os.path.basename(template_name)) diff --git a/src/git.py b/src/pygitver/git.py similarity index 100% rename from src/git.py rename to src/pygitver/git.py diff --git a/src/pygitver.py b/src/pygitver/pygitver.py similarity index 95% rename from src/pygitver.py rename to src/pygitver/pygitver.py index 96c5973..aede186 100644 --- a/src/pygitver.py +++ b/src/pygitver/pygitver.py @@ -1,7 +1,7 @@ import argparse -from git import Git, GitError -from changelogs_mngr import ChangelogsMngr, ChangelogsMngrError +from pygitver.git import Git, GitError +from pygitver.changelogs_mngr import ChangelogsMngr, ChangelogsMngrError import json @@ -116,9 +116,7 @@ def main(): output = join_changelogs.read_files(path=args.dir, file_ext="json") if args.format == "text": try: - print( - join_changelogs.changelog_generate(template_name=args.template) - ) + print(join_changelogs.generate(template_name=args.template)) except ChangelogsMngrError as err: print(err) exit(1) diff --git a/src/templates/changelog-common.tmpl b/src/pygitver/templates/changelog-common.tmpl similarity index 100% rename from src/templates/changelog-common.tmpl rename to src/pygitver/templates/changelog-common.tmpl diff --git a/src/templates/changelog.tmpl b/src/pygitver/templates/changelog.tmpl similarity index 100% rename from src/templates/changelog.tmpl rename to src/pygitver/templates/changelog.tmpl diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_changelogs_mngr.py b/tests/test_changelogs_mngr.py index b7ad8e6..9fb8ac3 100644 --- a/tests/test_changelogs_mngr.py +++ b/tests/test_changelogs_mngr.py @@ -1,7 +1,9 @@ +import os import json import unittest -from src.changelogs_mngr import ChangelogsMngr, Git, ChangelogsMngrError from unittest import mock +from pygitver.changelogs_mngr import ChangelogsMngr, ChangelogsMngrError +from pygitver.git import Git class TestChangelogsMngr(unittest.TestCase): @@ -14,10 +16,14 @@ def test_join_changelogs(self, monkeypatch): expected = json.load(fp) assert res == expected + @mock.patch.dict(os.environ, {"PYGITVER_TEMPLATE_CHANGELOG_COMMON": "src/pygitver/templates/changelog-common.tmpl"}, + clear=True) def test_join_changelogs_template(self): join_changelogs = ChangelogsMngr("2.1.2") join_changelogs.read_files("./tests/data/changelogs/", "json") - res = join_changelogs.changelog_generate(template_name="src/templates/changelog-common.tmpl") + + res = join_changelogs.generate() + expected = "# Change Log\n\n" \ "## Version: 3.0.0\n\n\n\n\n\n" \ "## Services\n\n\n\n" \ @@ -40,6 +46,12 @@ def test_join_changelogs_template(self): for pos, _ in enumerate(l_res): self.assertEqual(l_expected[pos], l_res[pos]) + res = join_changelogs.generate(template_name="src/pygitver/templates/changelog-common.tmpl") + l_res = res.split("\n") + l_expected = expected.split("\n") + for pos, _ in enumerate(l_res): + self.assertEqual(l_expected[pos], l_res[pos]) + def test_init_changelog(self): chl_mngr = ChangelogsMngr() chl_mngr._changelogs = {"version": "0.0.0", "services": {"test": None}} @@ -53,6 +65,6 @@ def test_changelog_generate(self): chl_mngr = ChangelogsMngr() with self.assertRaises(ChangelogsMngrError) as context: - chl_mngr.changelog_generate(template_name=template_name) + chl_mngr.generate(template_name=template_name) self.assertEqual(f"ERROR: Template '{template_name}' was not found.", str(context.exception)) diff --git a/tests/test_git.py b/tests/test_git.py index 6dadb41..883a27c 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -1,4 +1,4 @@ -from src.git import Git +from pygitver.git import Git GIT_LOG_OUTPUT_MOCK = "fix: test fix 1\n" \ "feat(api)!: new api\n" \ @@ -139,7 +139,7 @@ def test_changelog_generate(monkeypatch): monkeypatch.setattr(Git, "changelog", value=lambda *args, **kwargs: GIT_LOG_OUTPUT_MOCK) monkeypatch.setattr(Git, "git_version", value=lambda *args, **kwargs: "git version v1.0.0") changelog = Git.changelog_generate(changelog_group=Git.changelog_group(), - template_name="src/templates/changelog.tmpl") + template_name="src/pygitver/templates/changelog.tmpl") assert changelog == "##########\nChange Log\n##########\n\nVersion v1.0.0\n=============\n\n\n\n\n\n" \ "Features\n--------\n\n\n* New api\n\n\n\n\n\n" \ "Bug Fixes\n---------\n\n\n* Test fix 1\n\n* Test fix 2\n\n\n\n\n\n" \ From b9a0deb80c7b0dc7aedb77897cba7f7bc3400847 Mon Sep 17 00:00:00 2001 From: Yurii Puchkov Date: Wed, 27 Dec 2023 14:24:37 -0700 Subject: [PATCH 2/3] feat(CICD): Add pip build and push to the build steps --- .github/workflows/build.yaml | 50 ++++++++++++++++++++++++++++++------ pyproject.toml | 2 +- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 0a0108a..7ca7f0c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,4 +1,4 @@ -name: Docker Build & Push +name: Build & Push permissions: write-all on: @@ -41,12 +41,15 @@ jobs: pip install -U tox pip install tox tox + build: runs-on: ubuntu-latest needs: unittests if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Docker Build shell: bash @@ -55,19 +58,50 @@ jobs: docker build -t "${{ env.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}" . - name: Log in to Docker Hub - if: github.ref == 'refs/heads/main' uses: docker/login-action@v3 with: - username: ${{ env.DOCKER_USERNAME }} + username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} + - name: Detect package version + shell: bash + run: | + set -x + PACKAGE_VERSION="$(git tag --contains)" + if [[ "${PACKAGE_VERSION}" == "" ]]; then + PACKAGE_VERSION="$(docker run --rm -v $(pwd):/app -w /app --user "$(id -u):$(id -g)" panpuchkov/pygitver --next-ver)-dev" + PACKAGE_VERSION=$(echo "${PACKAGE_VERSION}" | sed 's/^v*//' ) + DOCKER_TAG_LATEST="${PACKAGE_VERSION}" + else + DOCKER_TAG_LATEST="latest" + fi + + echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV + echo "DOCKER_TAG_LATEST=$DOCKER_TAG_LATEST" >> $GITHUB_ENV + - name: Docker Push shell: bash run: | set -x - DOCKER_TAG="$(git tag --contains)" - if [[ "${DOCKER_TAG}" == "" ]]; then - DOCKER_TAG="latest" + # Generate docker tags + IMAGE_NAME_WITH_TAG_VERSION="${{ env.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${PACKAGE_VERSION}" + IMAGE_NAME_WITH_TAG_LATEST="${{ env.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${DOCKER_TAG_LATEST}" + + # Set docker tag + docker tag "${{ env.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:latest" "${{ env.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${PACKAGE_VERSION}" + docker push "${IMAGE_NAME_WITH_TAG_VERSION}" + + # Push latest if required + if [[ "${IMAGE_NAME_WITH_TAG_LATEST}" != "${IMAGE_NAME_WITH_TAG_LATEST}" ]]; then + docker push "${IMAGE_NAME_WITH_TAG_LATEST}" fi - IMAGE_NAME_WITH_VERSION="${{ env.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${DOCKER_TAG}" - docker push "${IMAGE_NAME_WITH_VERSION}" + + - name: Pip Build & Push + shell: bash + run: | + set -x + pip install -r requirements-build.txt + python -m build + sudo apt-get -y install twine + twine check dist/* + twine upload --skip-existing -u ${{ secrets.PYPI_USERNAME }} -p ${{ secrets.PYPI_PASSWORD }} dist/* --verbose diff --git a/pyproject.toml b/pyproject.toml index bec520b..169146e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pygitver" -version = "0.0.2" +version = "0.1.0-dev" authors = [ { name="Yurii Puchkov", email="panpuchkov@gmail.com" }, ] From 70933b64df60aca0a5cb9d15ce33fb5e468f0784 Mon Sep 17 00:00:00 2001 From: Yurii Puchkov Date: Wed, 27 Dec 2023 17:29:28 -0700 Subject: [PATCH 3/3] fix: unable to find default template for CHANGELOG generation --- pyproject.toml | 4 ++++ src/pygitver/changelogs_mngr.py | 4 +++- src/pygitver/git.py | 7 +++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 169146e..04e9b65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,3 +32,7 @@ pygitver = "pygitver.pygitver:main" [metadata] url = "https://github.com/panpuchkov/pygitver" + + +[tool.setuptools.package-data] +pygitver = ["*.py", "*.tmpl"] diff --git a/src/pygitver/changelogs_mngr.py b/src/pygitver/changelogs_mngr.py index dcc2f48..991d1ed 100644 --- a/src/pygitver/changelogs_mngr.py +++ b/src/pygitver/changelogs_mngr.py @@ -1,4 +1,5 @@ import os +import pathlib import json from pygitver.git import Git, GitError @@ -51,9 +52,10 @@ def read_files(self, path: str, file_ext: str = "json"): def generate(self, template_name: str = "") -> str: if not template_name: + script_directory = pathlib.Path(__file__).parent.resolve() template_name = os.getenv( "PYGITVER_TEMPLATE_CHANGELOG_COMMON", - "pygitver/templates/changelog-common.tmpl", + f"{script_directory}/templates/changelog-common.tmpl", ) try: env = Environment(loader=FileSystemLoader(os.path.dirname(template_name))) diff --git a/src/pygitver/git.py b/src/pygitver/git.py index 9c420a6..40fc0e3 100644 --- a/src/pygitver/git.py +++ b/src/pygitver/git.py @@ -1,5 +1,6 @@ -import json import os +import pathlib +import json import re import subprocess from jinja2 import Environment, FileSystemLoader, TemplateNotFound @@ -252,8 +253,10 @@ def changelog_generate(changelog_group: dict, template_name: str = "") -> str: :return: string with formatted changelog """ if not template_name: + script_directory = pathlib.Path(__file__).parent.resolve() template_name = os.getenv( - "PYGITVER_TEMPLATE_CHANGELOG", "templates/changelog.tmpl" + "PYGITVER_TEMPLATE_CHANGELOG", + f"{script_directory}/templates/changelog.tmpl", ) try: env = Environment(loader=FileSystemLoader(os.path.dirname(template_name)))