{"id":18084964,"url":"https://github.com/jwodder/pyrepo","last_synced_at":"2025-06-30T00:05:24.111Z","repository":{"id":37256396,"uuid":"183700366","full_name":"jwodder/pyrepo","owner":"jwodder","description":"Python repository templater \u0026 releaser","archived":false,"fork":false,"pushed_at":"2025-01-23T13:54:30.000Z","size":1157,"stargazers_count":7,"open_issues_count":41,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-16T13:05:33.210Z","etag":null,"topics":["github","hatch","packaging","pypi","python","template-project"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jwodder.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-26T22:16:43.000Z","updated_at":"2025-01-23T13:54:32.000Z","dependencies_parsed_at":"2023-10-30T23:30:41.675Z","dependency_job_id":"20e79dea-ecf0-474b-a479-ce4780fd6856","html_url":"https://github.com/jwodder/pyrepo","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/jwodder/pyrepo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fpyrepo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fpyrepo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fpyrepo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fpyrepo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jwodder","download_url":"https://codeload.github.com/jwodder/pyrepo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fpyrepo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262685663,"owners_count":23348451,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["github","hatch","packaging","pypi","python","template-project"],"created_at":"2024-10-31T15:08:48.865Z","updated_at":"2025-06-30T00:05:24.070Z","avatar_url":"https://github.com/jwodder.png","language":"Python","readme":"|repostatus| |ci-status| |coverage| |license|\n\n.. |repostatus| image:: https://www.repostatus.org/badges/latest/active.svg\n    :target: https://www.repostatus.org/#active\n    :alt: Project Status: Active — The project has reached a stable, usable\n          state and is being actively developed.\n\n.. |ci-status| image:: https://github.com/jwodder/pyrepo/actions/workflows/test.yml/badge.svg\n    :target: https://github.com/jwodder/pyrepo/actions/workflows/test.yml\n    :alt: CI Status\n\n.. |coverage| image:: https://codecov.io/gh/jwodder/pyrepo/branch/master/graph/badge.svg\n    :target: https://codecov.io/gh/jwodder/pyrepo\n\n.. |license| image:: https://img.shields.io/github/license/jwodder/pyrepo.svg\n    :target: https://opensource.org/licenses/MIT\n    :alt: MIT License\n\n`GitHub \u003chttps://github.com/jwodder/pyrepo\u003e`_\n| `Issues \u003chttps://github.com/jwodder/pyrepo/issues\u003e`_\n| `Changelog \u003chttps://github.com/jwodder/pyrepo/blob/master/CHANGELOG.md\u003e`_\n\n``jwodder-pyrepo`` is my personal command-line program for managing my Python\npackage repositories, including generating packaging boilerplate and performing\nreleases.  It is heavily dependent upon the conventions I use in building \u0026\nstructuring Python projects (documented in `the repository wiki\n\u003chttps://github.com/jwodder/pyrepo/wiki\u003e`__), and so it is not suitable for\ngeneral use.\n\n\nInstallation\n============\n``jwodder-pyrepo`` requires Python 3.10 or higher.  Just use `pip\n\u003chttps://pip.pypa.io\u003e`_ for Python 3 (You have pip, right?) to install it::\n\n    python3 -m pip install git+https://github.com/jwodder/pyrepo.git\n\n\nUsage\n=====\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] \u003ccommand\u003e ...\n\nAll ``pyrepo`` commands other than ``pyrepo init`` must be run inside a Python\nproject directory (after processing the ``--chdir`` option, if given); the\nproject root is determined by recursing upwards in search of a\n``pyproject.toml`` file.  Moreover, all commands other than ``pyrepo init``\nrequire that the project have already been set up by previously invoking\n``pyrepo init``.\n\n\nGlobal Options\n--------------\n\n-c FILE, --config FILE  Read configuration from ``FILE``; by default,\n                        configuration is read from ``~/.config/pyrepo.toml``\n\n-C DIR, --chdir DIR     Change to directory ``DIR`` before taking any further\n                        actions\n\n-l LEVEL, --log-level LEVEL\n                        Set the `logging level`_ to the given value; default:\n                        ``INFO``.  The level can be given as a case-insensitive\n                        level name or as a numeric value.\n\n                        This option can be set via the configuration file.\n\n.. _logging level: https://docs.python.org/3/library/logging.html\n                   #logging-levels\n\n\nConfiguration File\n------------------\n\nThe configuration file (located at ``~/.config/pyrepo.toml`` by default) is a\nTOML_ file with the following tables:\n\n.. _TOML: https://toml.io\n\n``[options]``\n    Sets default values for global options\n\n``[options.COMMAND]``\n   (where ``COMMAND`` is the name of a ``pyrepo`` subcommand) Sets default\n   values for options passed to ``pyrepo COMMAND``.\n\nNot all options can be configured via the configuration file; see the\ndocumentation for the respective options to find out which can.\n\nHyphens \u0026 underscores are interchangeable in option names in the configuration\nfile.\n\n\nGitHub Authentication\n---------------------\n\nThe ``init`` (if ``--github-user`` is not specified), ``mkgithub``, and\n``release`` subcommands make authenticated requests to the GitHub API and thus\nrequire a GitHub access token.  ``pyrepo`` will automatically search for a\nlocally-stored token when needed by consulting the following sources:\n\n- a ``.env`` file setting ``GH_TOKEN`` or ``GITHUB_TOKEN``\n- the ``GH_TOKEN`` or ``GITHUB_TOKEN`` environment variables\n- the gh_ command, if installed\n- the hub_ command's configuration file\n- the ``hub.oauthtoken`` Git config option\n\nIf no token is found in the above sources, ``pyrepo`` will error out.\n\n.. _gh: https://github.com/cli/cli\n.. _hub: https://github.com/mislav/hub\n\n\n``pyrepo init``\n---------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] init [\u003coptions\u003e] [\u003cdirectory\u003e]\n\nCreate packaging boilerplate for a new project (i.e., one that does not already\nhave a ``setup.py``, ``setup.cfg``, or ``pyproject.toml`` file) in\n``\u003cdirectory\u003e`` (default: the current directory).  The project must be in a Git\nrepository and already contain Python source code (either one flat module or\nelse a package containing an ``__init__.py`` file; either layout may optionally\nbe contained in a ``src/`` directory).  It is recommended to run this command\nin a clean Git repository (i.e., one without any pending changes) so that the\ncommand's effects can easily be reverted should anything go wrong.\n\n``pyrepo init`` ensures the code uses a ``src/`` layout — unless it's a flat\nmodule, in which case the ``src/`` layout is not used — and creates the\nfollowing files if they do not already exist:\n\n- ``.gitignore``\n- ``.pre-commit-config.yaml``\n- ``README.rst``\n- ``pyproject.toml``\n- ``tox.ini``\n\nIf a ``LICENSE`` file does not exist, one is created; otherwise, the copyright\nyears in the ``LICENSE`` file are updated.  In both cases, the copyright years\nin the ``LICENSE`` will contain the current year and all other years that\ncommits were made to the Git repository.\n\nA boilerplate docstring and project data variables (``__author__``,\n``__author_email__``, ``__license__``, ``__url__``, and ``__version__``) are\nalso added to the main source file (i.e., the only file if the project\nis a flat module, or the ``{{import_name}}/__init__.py`` file otherwise).\n\nIf there is a ``requirements.txt`` file and/or a ``__requires__ =\nlist_of_requirements`` assignment in the main source file, it is used to set\nthe project's dependencies in ``pyproject.toml`` and then deleted.  If both\nsources of requirements are present, the two lists are combined, erroring if\nthe same package is given two different requirement specifications.\n\nFinally, ``pre-commit install`` is run, and a message is printed instructing\nthe user to run ``pre-commit run -a`` after adding new files to the index.\n\n\nOptions\n^^^^^^^\n\nAll of the following can be set via the configuration file, in the\n``[options.init]`` table.\n\n--author NAME           Set the name of the project's author\n\n--author-email EMAIL    Set the project's author's e-mail address.  This may be\n                        either a plain e-mail address or a Jinja2 template\n                        defined in terms of the variables ``project_name`` and\n                        ``import_name``.\n\n--ci, --no-ci           Whether to generate templates for testing with GitHub\n                        Actions; implies ``--tests``; default: ``--no-ci``\n\n-c, --command NAME      If the project defines a command-line entry point, use\n                        this option to specify the name for the command.  The\n                        entry point will then be assumed to be at either\n                        ``IMPORT_NAME:main`` (if the code is a flat module) or\n                        ``IMPORT_NAME.__main__:main`` (if the code is a\n                        package).\n\n-d TEXT, --description TEXT\n                        Set the project's short description.  If no description\n                        is specified on the command line, the user will be\n                        prompted for one.\n\n--docs, --no-docs       Whether to generate templates for Sphinx documentation;\n                        default: ``--no-docs``\n\n--doctests, --no-doctests\n                        Whether to include running of doctests in the generated\n                        testing templates; only has an effect when ``--tests``\n                        is also given; default: ``--no-doctests``\n\n--github-user USER      Set the username to use in the project's GitHub and\n                        Codecov URLs; when not set, the user's GitHub login is\n                        retrieved using the GitHub API\n\n-p NAME, --project-name NAME\n                        Set the name of the project as it will be known on\n                        PyPI; defaults to the import name.\n\n                        This can be set to a Jinja2 template defined in terms\n                        of the variable ``import_name``.\n\n-P SPEC, --python-requires SPEC\n                        Set the project's ``requires-python`` value.  ``SPEC``\n                        may be either a PEP 440 version specifier (e.g., ``\u003e=\n                        3.3, != 3.4.0``) or a bare ``X.Y`` version (to which\n                        ``\u003e=`` will be prepended).  When not specified on the\n                        command line, this value is instead extracted from\n                        either a \"``# Python SPEC``\" comment in\n                        ``requirements.txt`` or a ``__python_requires__ =\n                        'SPEC'`` assignment in the main source file; it is an\n                        error if these sources have different values.  If none\n                        of these sources are present, ``pyrepo init`` falls\n                        back to the value of ``requires-python`` in the\n                        ``[options.init]`` table of the configuration file,\n                        which in turn defaults to ``\u003e=`` plus the current\n                        minimum supported Python series.\n\n                        Besides setting ``requires-python``, the value of this\n                        option will also be applied as a filter to all\n                        currently-supported Python series in order to determine\n                        what Python series to include classifiers for in\n                        ``pyproject.toml`` and what series to test against with\n                        tox and CI.\n\n--repo-name NAME        The name of the project's repository on GitHub;\n                        defaults to the project name.\n\n                        This can be set to a Jinja2 template defined in terms\n                        of the variables ``project_name`` and ``import_name``.\n\n--rtfd-name NAME        The name of the project's Read the Docs site; defaults\n                        to the project name.\n\n                        This can be set to a Jinja2 template defined in terms\n                        of the variables ``project_name`` and ``import_name``.\n\n--tests, --no-tests     Whether to generate templates for testing with pytest\n                        and tox; default: ``--no-tests``\n\n--typing, --no-typing   Whether to include configuration for type annotations\n                        (creating a ``py.typed`` file, adding a ``typing``\n                        testenv to ``tox.ini`` if ``--tests`` is set, adding a\n                        ``typing`` job to the CI configuration if ``--ci`` is\n                        set, etc.); default: ``--no-typing``\n\n\n``pyrepo add-ci-testenv``\n-------------------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] add-ci-testenv \u003ctestenv\u003e \u003cpython-version\u003e\n\nConfigure the GitHub Actions test workflow to include a run of the tox\nenvironment ``\u003ctestenv\u003e`` against ``\u003cpython-version\u003e``.\n\n\n``pyrepo add-pyversion``\n------------------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] add-pyversion \u003cversion\u003e ...\n\nConfigure the project to declare support for and test against the given Python\nversion(s) (which must be given in the form \"``X.Y``\").\n\nNote that this command will not modify the project's ``requires-python``\nsetting.  If a given version is out of bounds for ``requires-python``, an error\nwill result; update ``requires-python`` and try again.\n\n\n``pyrepo add-typing``\n---------------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] add-typing\n\n\nAdd configuration for type annotations and the checking thereof:\n\n- Add a ``py.typed`` file to the Python package (after converting from a flat\n  module, if necessary)\n\n- Add a \"``Typing :: Typed``\" classifier to the project classifiers\n\n- Add a ``mypy`` configuration section to ``pyproject.toml``\n\n- Add a ``typing`` testenv to ``tox.ini`` if tests are enabled\n\n- Add a ``typing`` job (run against the lowest supported Python version) to the\n  CI configuration if it exists\n\n\n``pyrepo begin-dev``\n--------------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] begin-dev [\u003coptions\u003e]\n\nPrepare for development on the next version of a project by setting\n``__version__`` to the next minor version number plus \".dev1\" and adding a new\nsection to the top of the CHANGELOG (creating a CHANGELOG if necessary) and to\nthe top of ``docs/changelog.rst`` (creating it if a ``docs`` directory already\nexists).  This is the same behavior as the last step of ``pyrepo release``.\n\nIf the project uses versioningit_, the ``__version__`` variable is left alone.\n\nIf the project is already in \"dev mode\", nothing is done.\n\nOptions\n^^^^^^^\n\n-N, --no-next-version           Do not calculate the next version for the\n                                project: set ``__version__`` (if not using\n                                versioningit) to the current version plus\n                                \".post1\" and omit the version from the new\n                                CHANGELOG section\n\n\n``pyrepo drop-pyversion``\n-------------------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] drop-pyversion\n\nConfigure the project to no longer declare support for or test against the\ncurrent lowest supported minor Python version.\n\nIt is an error to run this command when the project declares support for only\nzero or one minor Python version.\n\n\n``pyrepo inspect``\n------------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] inspect\n\nExamine a project repository and output its template variables as a JSON\nobject.  This command is primarily intended for debugging purposes.\n\n\n``pyrepo mkgithub``\n-------------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] mkgithub [\u003coptions\u003e]\n\nCreate a new GitHub repository for the project; set the repository's\ndescription to the project's short description; set the repository's topics to\nthe project's keywords plus \"python\"; create \"dependencies\",\n\"d:github-actions\", and \"d:python\" labels in the repository (if\n``.github/dependabot.yml`` exists); set the ``CODECOV_TOKEN`` secret for GitHub\nActions (including for Dependabot); set the local repository's ``origin``\nremote to point to the GitHub repository; and push all branches \u0026 tags to the\nremote.\n\n\nOptions\n^^^^^^^\n\n--codecov-token SECRET  Value to use for the ``CODECOV_TOKEN`` secret.  If this\n                        value is not set and ``--no-codecov-token`` is not\n                        given, a warning is emitted.\n\n                        This option can be set via the ``CODECOV_TOKEN``\n                        environment variable or via the configuration file.\n\n--no-codecov-token      Do not set the ``CODECOV_TOKEN`` secret.\n\n-P, --private           Make the new repository private.\n\n--repo-name NAME        The name of the new repository; defaults to the\n                        repository name used in the project's URL.\n\n\n``pyrepo release``\n------------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] release [\u003coptions\u003e] [\u003cversion\u003e]\n\nCreate \u0026 publish a new release for a project.  This command performs the\nfollowing operations in order:\n\n- If the version for the new release is not specified on the command line, it\n  is calculated by removing any prerelease \u0026 dev components from the project's\n  current version\n- If the project does not use versioningit_, set ``__version__`` to the version\n  of the new release\n- If a CHANGELOG exists, set the date for the newest version section\n- If ``docs/changelog.rst`` exists, set the date for the newest version section\n- Update the copyright year ranges in ``LICENSE`` and (if present)\n  ``docs/conf.py`` to include all years in which commits were made to the\n  repository\n- If there is no CHANGELOG file, assume this is the first release and:\n\n  - Update the repostatus badge in the README from \"WIP\" to \"Active\"\n  - If the project does not have a \"Private\" classifier, remove the\n    \"work-in-progress\" topic from the repository on GitHub and add the topic\n    \"available-on-pypi\"\n\n- If the ``--tox`` option is given, run tox, failing if it fails\n- Build the sdist \u0026 wheel\n- Run ``twine check`` on the sdist \u0026 wheel\n- Commit all changes made to the repository; the most recent CHANGELOG section\n  is included in the commit message template.  The commit is then tagged \u0026\n  signed.\n\n  - The release can be cancelled at this point by leaving the commit message\n    unchanged.\n\n  - If the project uses ``versioningit``, this step is moved to before building\n    the sdist \u0026 wheel.\n\n- Push the commit \u0026 tag to GitHub\n- Convert the tag to a release on GitHub, using the commit message for the name\n  and body\n- If the project does not have a \"Private\" classifier, upload the build assets\n  to PyPI\n- Upload the build assets to GitHub as release assets\n- Prepare for development on the next version by setting ``__version__`` to the\n  next minor version number plus \".dev1\" and adding a new section to the top of\n  the CHANGELOG (creating a CHANGELOG if necessary) and to the top of\n  ``docs/changelog.rst`` (creating it if a ``docs`` directory already exists)\n\n  If the project uses versioningit_, the ``__version__`` variable is left\n  alone.\n\n\nOptions\n^^^^^^^\n\n--tox, --no-tox         Whether to run ``tox`` on the project before building;\n                        default: ``--no-tox``.\n\n                        This option can be set via the configuration file.\n\n--major                 Set the release's version to the next major version\n\n--minor                 Set the release's version to the next minor version\n\n--micro                 Set the release's version to the next micro/patch\n                        version\n\n--post                  Set the release's version to the next post version\n\n--date                  Set the release's version to the current date in\n                        ``YYYY.MM.DD`` format\n\n\n``pyrepo template``\n-------------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] template [\u003coptions\u003e] \u003ctemplated-file\u003e ...\n\nReplace the given files with their re-evaluated templates.\n\n\nOptions\n^^^^^^^\n\n-o FILE, --outfile FILE\n                        Write output to ``\u003cfile\u003e`` instead of overwriting the\n                        file given on the command line.  This option may only\n                        be used when exactly one argument is given on the\n                        command line.\n\n\n``pyrepo unflatten``\n--------------------\n\n::\n\n    pyrepo [\u003cglobal-options\u003e] unflatten\n\nConvert a \"flat module\" project (one where all the code is in a ``foobar.py``\nfile) to a \"package\" project (one where all the code is in a ``src/foobar/``\ndirectory containing an ``__init__.py`` file).  The old flat module becomes the\n``__init__.py`` file of the new package directory, and the project's\n``pyproject.toml`` and ``tox.ini`` are updated for the change in configuration.\n\n\nRestrictions\n============\n``jwodder-pyrepo`` relies on various assumptions about project layout and\nformatting; see `the project wiki on GitHub`__ for details.  Most notably, it\ndoes not support the following types of projects:\n\n__ https://github.com/jwodder/pyrepo/wiki/Project-Layout-Specification\n\n- projects that do not use hatch\n- projects with packages that do not use a ``src/`` layout\n- projects with flat modules that use a ``src/`` layout\n- projects that neither store their version in a ``__version__`` variable in\n  the initfile nor use versioningit_\n- projects that are not pure Python\n- projects containing more than one root-level module/package\n- namespace packages\n- (``pyrepo init``) projects that support Python 2\n- (``pyrepo release``) projects that only support Python 2\n\n.. _versioningit: https://github.com/jwodder/versioningit\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjwodder%2Fpyrepo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjwodder%2Fpyrepo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjwodder%2Fpyrepo/lists"}