diff --git a/.git_archival.txt b/.git_archival.txt new file mode 100644 index 00000000..7c510094 --- /dev/null +++ b/.git_archival.txt @@ -0,0 +1,3 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..00a7b00c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +.git_archival.txt export-subst diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b4ca4e41..1866b0f3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,28 +1,21 @@ -name: release +name: 🚀 Release on: release: types: [published] - workflow_dispatch: - inputs: - ref: - description: 'Tag to release' - required: true jobs: release: + name: 🚀 Release runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/fmf + permissions: + id-token: write # For pypi-publish steps: - - uses: actions/checkout@v2 - if: ${{ github.event_name == 'release' }} - - uses: actions/checkout@v2 - if: ${{ github.event_name == 'workflow_dispatch' }} - with: - ref: ${{ github.event.inputs.ref }} - - name: Create dist - run: make wheel + - uses: actions/checkout@v4 + - name: Build package + run: pipx hatch build - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.packit.yaml b/.packit.yaml index cf35ac09..79d06d9a 100644 --- a/.packit.yaml +++ b/.packit.yaml @@ -5,15 +5,15 @@ synced_files: upstream_package_name: fmf downstream_package_name: fmf +# Epel9 fails to build because of too old version of setuptools_scm +# Need to create archive with PKG-INFO actions: create-archive: - - make tarball - get-current-version: - - make version + - "hatch build -t sdist" + - "sh -c 'echo dist/fmf-*.tar.gz'" srpm_build_deps: - - make - - python3-docutils + - hatch jobs: - job: copr_build diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index b4ac02bd..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include fmf.spec diff --git a/Makefile b/Makefile index 9ee405b3..925164d7 100644 --- a/Makefile +++ b/Makefile @@ -1,53 +1,42 @@ # Prepare variables TMP = $(CURDIR)/tmp -VERSION = $(shell grep ^Version fmf.spec | sed 's/.* //') -COMMIT = $(shell git rev-parse --short HEAD) -REPLACE_VERSION = "s/running from the source/$(VERSION) ($(COMMIT))/" +VERSION = $(shell hatch version) PACKAGE = fmf-$(VERSION) FILES = LICENSE README.rst \ - Makefile fmf.spec setup.py \ - examples fmf bin tests + Makefile fmf.spec pyproject.toml \ + examples fmf tests # Define special targets all: docs packages -.PHONY: docs hooks +.PHONY: docs hooks tmp # Temporary directory, include .fmf to prevent exploring tests there tmp: mkdir -p $(TMP)/.fmf + mkdir -p $(TMP)/$(PACKAGE) # Run the test suite, optionally with coverage test: tmp - pytest tests/unit -c tests/unit/pytest.ini + hatch run test:unit smoke: tmp - pytest tests/unit/test_smoke.py -c tests/unit/pytest.ini + hatch run test:smoke coverage: tmp - coverage run --source=fmf,bin -m py.test -c tests/unit/pytest.ini tests - coverage report - coverage annotate + hatch run cov:cov # Build documentation, prepare man page docs: man - cd docs && make html -man: source + hatch run docs:html +man: tmp cp docs/header.txt $(TMP)/man.rst tail -n+7 README.rst >> $(TMP)/man.rst rst2man $(TMP)/man.rst > $(TMP)/$(PACKAGE)/fmf.1 # RPM packaging -source: clean tmp - mkdir -p $(TMP)/SOURCES - mkdir -p $(TMP)/$(PACKAGE) - cp -a $(FILES) $(TMP)/$(PACKAGE) - sed -i $(REPLACE_VERSION) $(TMP)/$(PACKAGE)/fmf/__init__.py -tarball: source man - cd $(TMP) && tar cfz SOURCES/$(PACKAGE).tar.gz $(PACKAGE) - @echo ./tmp/SOURCES/$(PACKAGE).tar.gz -version: - @echo "$(VERSION)" +tarball: man + hatch build -t sdist rpm: tarball rpmbuild --define '_topdir $(TMP)' -bb fmf.spec srpm: tarball @@ -57,10 +46,9 @@ packages: rpm srpm # Python packaging wheel: - python setup.py bdist_wheel - python3 setup.py bdist_wheel -upload: - twine upload dist/*.whl + hatch build -t wheel +upload: wheel tarball + hatch publish # Vim tags and cleanup diff --git a/bin/fmf b/bin/fmf deleted file mode 100755 index 0b8a45df..00000000 --- a/bin/fmf +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# fmf - Flexible Metadata Format -# Author: Petr Šplíchal -# -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Copyright (c) 2018 Red Hat, Inc. -# -# This program is free software: you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation, either version 2 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be -# useful, but WITHOUT ANY WARRANTY; without even the implied -# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -# PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see http://www.gnu.org/licenses/. -# -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -import sys - -import fmf.base -import fmf.cli -import fmf.utils - -try: - fmf.cli.main() -except fmf.utils.GeneralError as error: - if "--debug" in sys.argv: - raise - fmf.utils.log.error(error) - raise SystemExit(1) diff --git a/fmf.spec b/fmf.spec index ae8b0cb1..abca36f7 100644 --- a/fmf.spec +++ b/fmf.spec @@ -1,16 +1,21 @@ -Name: fmf -Version: 1.4.1 -Release: 1%{?dist} +Name: fmf +Version: 0.0.0 +Release: 1%{?dist} -Summary: Flexible Metadata Format -License: GPLv2+ -BuildArch: noarch +Summary: Flexible Metadata Format +License: GPL-2.0-or-later +BuildArch: noarch -URL: https://github.com/psss/fmf -Source0: https://github.com/psss/fmf/releases/download/%{version}/fmf-%{version}.tar.gz +URL: https://github.com/teemtee/fmf +Source: %{pypi_source fmf} # Main fmf package requires the Python module -Requires: python%{python3_pkgversion}-%{name} == %{version}-%{release} +BuildRequires: python3-devel +BuildRequires: python3dist(docutils) +BuildRequires: git-core +Requires: git-core + +%py_provides python3-fmf %description The fmf Python module and command line tool implement a flexible @@ -18,62 +23,39 @@ format for defining metadata in plain text files which can be stored close to the source code. Thanks to hierarchical structure with support for inheritance and elasticity it provides an efficient way to organize data into well-sized text documents. -This package contains the command line tool. - -%?python_enable_dependency_generator - -%package -n python%{python3_pkgversion}-%{name} -Summary: %{summary} -BuildRequires: python%{python3_pkgversion}-devel -BuildRequires: python%{python3_pkgversion}-setuptools -BuildRequires: python%{python3_pkgversion}-pytest -BuildRequires: python%{python3_pkgversion}-ruamel-yaml -BuildRequires: python%{python3_pkgversion}-filelock -BuildRequires: python%{python3_pkgversion}-jsonschema -BuildRequires: git-core -%{?python_provide:%python_provide python%{python3_pkgversion}-%{name}} -Requires: git-core -%description -n python%{python3_pkgversion}-%{name} -The fmf Python module and command line tool implement a flexible -format for defining metadata in plain text files which can be -stored close to the source code. Thanks to hierarchical structure -with support for inheritance and elasticity it provides an -efficient way to organize data into well-sized text documents. -This package contains the Python 3 module. +%prep +%autosetup -p1 -n fmf-%{version} -%prep -%autosetup +%generate_buildrequires +%pyproject_buildrequires -x tests %{?epel:-w} %build -%py3_build +%pyproject_wheel +cp docs/header.txt man.rst +tail -n+7 README.rst >> man.rst +rst2man man.rst > fmf.1 %install -%py3_install +%pyproject_install +%pyproject_save_files fmf + mkdir -p %{buildroot}%{_mandir}/man1 install -pm 644 fmf.1* %{buildroot}%{_mandir}/man1 %check -%{__python3} -m pytest -vv -c tests/unit/pytest.ini -m 'not web' - +%pyproject_check_import -%{!?_licensedir:%global license %%doc} -%files +%files -f %{pyproject_files} %{_mandir}/man1/* %{_bindir}/%{name} %doc README.rst examples -%license LICENSE - -%files -n python%{python3_pkgversion}-%{name} -%{python3_sitelib}/%{name}/ -%{python3_sitelib}/%{name}-*.egg-info -%license LICENSE %changelog diff --git a/fmf/__init__.py b/fmf/__init__.py index bdca5f5d..12af8b89 100644 --- a/fmf/__init__.py +++ b/fmf/__init__.py @@ -1,14 +1,17 @@ """ Flexible Metadata Format """ -# Version is replaced before building the package -__version__ = 'running from the source' +from __future__ import annotations + +import importlib.metadata + +from fmf.base import Tree +from fmf.context import Context +from fmf.utils import filter + +__version__ = importlib.metadata.version("fmf") __all__ = [ "Context", "Tree", "filter", ] - -from fmf.base import Tree -from fmf.context import Context -from fmf.utils import filter diff --git a/fmf/cli.py b/fmf/cli.py index 7fd1804d..6b28f2dd 100644 --- a/fmf/cli.py +++ b/fmf/cli.py @@ -209,3 +209,13 @@ def main(arguments=None, path=None): """ Parse options, do what is requested """ parser = Parser(arguments, path) return parser.output + + +def cli_entry(): + try: + main() + except fmf.utils.GeneralError as error: + if "--debug" not in sys.argv: + fmf.utils.log.error(error) + raise SystemExit(1) + raise diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..4bb236b3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,105 @@ +[build-system] +requires = ['hatchling', 'hatch-vcs'] +build-backend = 'hatchling.build' + +[project] +name = 'fmf' +authors = [ + { name = 'Petr Splichal', email = 'psplicha@redhat.com' }, +] +maintainers = [ + { name = 'Petr Splichal', email = 'psplicha@redhat.com' }, +] +description = 'Flexible Metadata Format' +readme = 'README.rst' +license = 'GPL-2.0-or-later' +license-files = { paths = ['LICENSE'] } +requires-python = '>=3.9' +classifiers = [ + 'Natural Language :: English', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Topic :: Utilities', +] +keywords = [ + 'metadata', + 'testing', +] +dependencies = [ + 'ruamel.yaml', + 'filelock', + 'jsonschema', +] +dynamic = ['version'] + +[project.urls] +homepage = 'https://github.com/teemtee/fmf' +documentation = 'https://fmf.readthedocs.io' + +[project.optional-dependencies] +# Needed for tests inside rpm build. Not being packaged in rpm +tests = [ + 'pytest', +] +# Needed for readthedocs and man page build. Not being packaged in rpm. +docs = [ + 'sphinx', + 'sphinx_rtd_theme', +] + +[project.scripts] +fmf = 'fmf.cli:cli_entry' + +[tool.hatch] +version.source = 'vcs' + +[tool.hatch.envs.default] +platforms = ["linux"] + +[tool.hatch.envs.dev] +description = "Development environment" +dependencies = [ + "pytest-cov" +] +features = ["tests"] + +[tool.hatch.envs.dev.scripts] +type = ["mypy {args:tmt}"] +check = ["lint", "type"] + +unit = "pytest -vvv -ra --showlocals tests/unit" +smoke = "pytest -vvv -ra --showlocals tests/unit/test_cli.py" + +cov = [ + "coverage run --source=fmf -m pytest -vvv -ra --showlocals tests", + "coverage report", + "coverage annotate", + ] + +[tool.hatch.envs.dev-not-editable] +template = "dev" +description = "Same as 'dev', but not using editable install" +dev-mode = false + +[tool.hatch.envs.test] +template = "dev" +description = "Run scripts with multiple Python versions" + +[[tool.hatch.envs.test.matrix]] +python = ["3.9", "3.11", "3.12"] + +[tool.hatch.envs.docs] +features = ["docs"] + +[tool.hatch.envs.docs.scripts] +html = "sphinx-build -b html {root}/docs {root}/docs/_build {args}" + +[tool.pytest.ini_options] +markers = [ + "web: tests which need to access the web", +] +testpaths = [ + 'tests', +] diff --git a/setup.py b/setup.py deleted file mode 100755 index da308297..00000000 --- a/setup.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python - -import re -from io import open - -from setuptools import setup - -# Parse version from the spec file -with open('fmf.spec', encoding='utf-8') as specfile: - lines = "\n".join(line.rstrip() for line in specfile) - version = re.search('Version: (.+)', lines).group(1).rstrip() - -# acceptable version schema: major.minor[.patch][sub] -__version__ = version -__pkg__ = 'fmf' -__pkgdir__ = {} -__pkgs__ = ['fmf'] -__provides__ = ['fmf'] -__desc__ = 'Flexible Metadata Format' -__scripts__ = ['bin/fmf'] - -# Prepare install requires and extra requires -install_requires = [ - 'ruamel.yaml', - 'filelock', - 'jsonschema', - ] -extras_require = { - 'docs': ['sphinx==7.2.4', 'sphinx-rtd-theme==1.3.0'], - 'tests': ['pytest', 'python-coveralls', 'pre-commit'], - } -extras_require['all'] = [ - dependency - for extra in extras_require.values() - for dependency in extra] - -pip_src = 'https://pypi.python.org/packages/source' -__deplinks__ = [] - -# README is in the parent directory -readme = 'README.rst' -with open(readme, encoding='utf-8') as _file: - readme = _file.read() - -github = 'https://github.com/psss/fmf' -download_url = '{0}/archive/master.zip'.format(github) - -default_setup = dict( - url=github, - license='GPLv2', - author='Petr Splichal', - author_email='psplicha@redhat.com', - maintainer='Petr Splichal', - maintainer_email='psplicha@redhat.com', - download_url=download_url, - long_description=readme, - data_files=[], - classifiers=[ - 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', - 'Natural Language :: English', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Topic :: Utilities', - ], - keywords=['metadata', 'testing'], - dependency_links=__deplinks__, - description=__desc__, - install_requires=install_requires, - extras_require=extras_require, - name=__pkg__, - package_dir=__pkgdir__, - packages=__pkgs__, - provides=__provides__, - scripts=__scripts__, - version=__version__, - ) - -setup(**default_setup) diff --git a/tests/unit/pytest.ini b/tests/unit/pytest.ini deleted file mode 100644 index cafe5e5d..00000000 --- a/tests/unit/pytest.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pytest] -markers = - web: tests which need to access the web