Package: example-package-grumbit
Python Packaging ExampleThis is a working example that uses a GitHub actions CI/CD workflow to test, build and upload a Python package to TestPyPi and PyPi.I created this example package by working through these guides;Packaging Python ProjectsPublishing package distribution releases using GitHub Actions CI/CD workflowsPyTest With GitHub ActionsThe rest of the README describes to set up a new project in the same way.When set up;Test and upload to TestPyPi occurs when thepackage version numberis updated and a commit is made to the master branchTest and upload to PyPi occurs when acommit is taggedThe package can be installed usingpip install example-package-grumbitTable of contentsHigh levelPackaging Python ProjectsBuilding the packageUploading the package to TestPyPiUploading the package to PyPiManually updating the packageGitHub Actions CI/CD workflowsSet upUploading toTestPyPivia commit to masterUploading toPyPivia taggingRunning pytest in GitHub CI/CDHigh levelAt a high level, the process for setting up GitHub CI/CD project packaging, including pytesting, looks like this;Set up the file structure as per this example package, but leave out./.github/workflows/publish-to-test-pypi.ymlfor now.Getlocal packagingworkingGetuploading to TestPyPiworkingGetuploading to PyPiworkingAdd in./.github/workflows/publish-to-test-pypi.ymland getGitHub CI/CDworkingPackaging Python ProjectsBuilding the packageThe packagemetadata is configuredin./pyproject.tomlOnce configured, it can be built with;cd<pacakgesdirectory>
python3-mvenv.venv# Create the venv if it doesn't exist yetsource.venv/bin/activate
python3-mpipinstall--upgradepipsetuptoolswheelpip-toolspytest# Install the tools needed for the build toolpython3-mpipinstall--upgradebuild# Install the build tool itselfpython3-mbuild# build the packageUploading the package to TestPyPiUpload the package for testing using;python3-mpipinstall--upgradetwine# Install the twine upload toolpython3-mtwineupload--repositorytestpypidist/*# Upload to TestPyPi# When prompted, the username is __token__ and the password is the TestPyPi global scope API tokenHaving uploaded the package, a package specificAPI token should be set up and savedinTestPyPiCheck the package can be downloaded and used in a newvenv;cd<somenewtmpdirectory>
python3-mvenv.venvsource.venv/bin/activatepackage_name="example-package-grumBit"python3-mpipinstall--index-urlhttps://test.pypi.org/simple/--pre${package_name}# Check the package can be installedpython3-c"from example_package_grumbit import example; print(example.add_one(1))"# Check package functionsUploading the package to PyPipython3-mtwineuploaddist/*# Upload to PyPi# When prompted, the username is __token__ and the password is the PyPi global scope API tokenHaving uploaded the package, a package specificAPI token should be set up and savedinPyPiManually updating the packageEach time the package is updated, it's version must be updated in the[project]section of./pyproject.toml, then it needs to be re-built and uploaded;vs./pyproject.toml
python3-mbuild# build the packagepython3-mtwinecheckdist/*# check the package can be uploadedpython3-mtwineupload--repositorytestpypidist/*# test uploading using TestPyPipython3-mtwineuploaddist/*# Upload to PyPiGitHub Actions CI/CD workflowsSet upIf the project isn't already sync'd up to GitHub, run;cd"<the project's directory>"repo_name="<the new repo's name>"ghrepocreate"${repo_name}"--private
gitinit
gitadd--all
gitcommit-m"init"gitbranch-Mmaster
gitremoteaddorigingit@github.com:grumBit/${repo_name}.git
gitpush-uoriginmasterIf the default branch isn'tmaster, eitherchange it on GitHub, or change.github/workflows/publish-to-test-pypi.yml.Add the TestPyPi and PyPi API tokens to the repoOpen the repo on GitHub usinggh browse. In the browser, clickSettings->Secrets->Actions. Then add two new secrets calledPYPI_API_TOKENandTEST_PYPI_API_TOKEN, with the API tokens created after uploading the packages aboveCreate and configure.github/workflows/publish-to-test-pypi.ymlworkflow definitionNB: This example package'spublish-to-test-pypi.ymlalready has the parts needed for auto-testing included (see below)Uploading toTestPyPivia commit to masterEvery time a commit is made to themasterbranch, the GitHub CI/CD will run.NB: For the packaging to succeed, the version must be updated in./pyproject.toml.All commits to master will be uploaded toTestPyPiUploading toPyPivia taggingPutting a tag on a commit and pushing it will cause GitHub CI/CD to run and create a PyPi release.Use the following to tag the lastest commit (i.e.HEAD) with the version currently configured in./pyproject.toml;version_tag=v$(cat./pyproject.toml|egrep"^version"|cut-d'"'-f2)version_tag_info="Some release info"gittag-a"${version_tag}"-m"${version_tag_info}"gitpush--tagUse the following to tag a prior commit;version_tag="vX.X.X"version_tag_info="Some release info"commit_sha="16fb0fd"gittag-a"${version_tag}""${commit_sha}"-m"${version_tag_info}"gitpush--tagRunning pytest in GitHub CI/CDThe workflow steps in theGitHub CI/CDguide didn't include running pytests. To get pytest to run the packages dependencies needed to be installed and then pytest run prior to the build step using these additional steps;-name:Install requirementsrun:>-python -mpip install--requirement requirements.txt-name:Run testsrun:>-python -mpytestAs per the directory structure in thepackaging projectsguide, I put the tests in a separate hierarchy to the source. This meant__init__.pyneeded to added to thesrc/andtests/directory like this;NB: I'm not 100% sure about this structuring. It follows the guide and means the test code isn't packaged up, however, I think it's less convenient than embeddingtest/folders within thesrc/tree../pyproject.tomlcan be configured so that embeddedtest/folders are excluded, but I've gone with the "standard" for now.packaging_tutorial/
├── src/
│   ├── __init__.py
│   └── example_package_grumbit/
│       ├── __init__.py
│       └── example.py
└── tests/
    ├── __init__.py
    └── example_package_grumbit/
        ├── __init__.py
        └── test_example.py