{"id":159,"date":"2024-08-13T15:36:19","date_gmt":"2024-08-13T15:36:19","guid":{"rendered":"https:\/\/platformdev.org\/?p=159"},"modified":"2024-09-09T01:45:49","modified_gmt":"2024-09-09T01:45:49","slug":"python-ci-with-github-actions","status":"publish","type":"post","link":"https:\/\/platformdev.org\/index.php\/2024\/08\/13\/python-ci-with-github-actions\/","title":{"rendered":"Python CI with Github Actions"},"content":{"rendered":"\n<p>When developing projects in python, it&#8217;s important to have a reliable CI workflow that can perform the following functions. <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-style-default has-kenta-base-100-background-color has-background\">\n<ul>\n<li>Linting and Formatting<\/li>\n\n\n\n<li>Unit Tests with Code Coverage<\/li>\n\n\n\n<li>Security Scanning for potential vulnerabilities<\/li>\n\n\n\n<li>Building the source code<\/li>\n\n\n\n<li>Deploying the build to a non-production environment for integration tests<\/li>\n<\/ul>\n<\/blockquote>\n\n\n\n<p>Before we dive into these steps,  let&#8217;s briefly discuss our branching \/ release strategy.  The most common strategies are GitFlow, Github flow, Trunk based, Gitlab flow, and variations of each.  For the majority of my projects I prefer a trunk-based strategy with small, frequent updates to my main production branch. <\/p>\n\n\n\n<p>A typical change in the production source code starts with pulling down <code>main<\/code> and creating a new branch. Let&#8217;s call the new branch <code>feature-request-1<\/code>.  I&#8217;ll make my changes to the source code in this branch and when I am ready for the code to be merged into <code>main<\/code>, I will submit a pull request.   <\/p>\n\n\n\n<p>The pull-request to <code>main<\/code> is an event in Github actions that we will use to trigger a series of steps in our python workflow.  <\/p>\n\n\n\n<pre class=\"wp-block-code has-kenta-primary-active-background-color has-background has-small-font-size\"><code><strong>Step 1.<\/strong> Lint and Format our code using pylint and black. This will make our code easy to read in a standard format and also check for syntax errors. <\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code has-kenta-primary-active-background-color has-background has-small-font-size\"><code><strong>Step 2.<\/strong> Run our unit tests. As a rule of thumb, I strive for 80% of my code to have test coverage. It's not always feasible with large codebases, however greater coverage ensures the code is functioning as intended. <\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code has-kenta-primary-active-background-color has-background has-small-font-size\"><code><strong>Step 3.<\/strong> Security scanning using Github or other 3rd party tools to find any vulnerabilities in the code before we build.  It's a good idea to fail the CI process here if the number of vulnerabilities exceed a defined threshold. <\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code has-kenta-primary-active-background-color has-background has-small-font-size\"><code><strong>Step 4.<\/strong> Execute the python build process to build our source package.  I prefer using the Github Commit SHA as the version number.  This is achieved by updating the <span style=\"background-color: initial; font-family: inherit; font-size: inherit; color: var(--wp--preset--color--kenta-base-100);\">pyproject.toml<\/span> file with dynamic versioning using <a style=\"background-color: initial; font-family: inherit; font-size: inherit;\" href=\"https:\/\/setuptools-scm.readthedocs.io\/en\/latest\/\">setuptools-scm<\/a><\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code has-kenta-primary-active-background-color has-background has-small-font-size\"><code><strong>Step 5.<\/strong> Deploy our source code package to our Development environment for further testing. Once deployed, pull request reviewers (other team members) can run integration tests to validate the integrity of the code before approving and merging into the <span style=\"background-color: initial; font-family: inherit; font-size: inherit; color: var(--wp--preset--color--kenta-base-100);\">main<\/span> branch. <\/code><\/pre>\n\n\n\n<p>We can chain these jobs sequentially or have them execute independently. It&#8217;s also a good practice to write the results of these jobs as a comment on the pull request so that other code reviewers or approvers can quickly see that these jobs have succeeded before reviewing the code itself.  The pull request can be rejected if our jobs fail, saving time for those required to review. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Artifacts<\/h3>\n\n\n\n<p>Python packages can be stored as GitHub artifacts and used in subsequent jobs. <\/p>\n\n\n\n<p>Python build packages are initially created with the Commit SHA as the version to quickly identify each package with a specific commit.  Release versions will be built using tags created with semantic versioning. <\/p>\n\n\n\n<blockquote class=\"wp-block-quote has-kenta-base-100-background-color has-background\">\n<p>Github Packages does not support PyPi repositories, therefore I use AWS CodeArtifact since most of my projects are deployed in AWS.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Release Process<\/h3>\n\n\n\n<p>Once the pull request is approved and the commit is merged into <code>main<\/code>, at this point it&#8217;s ready for a Release. <\/p>\n\n\n\n<p>The source of the release is <code><strong>main<\/strong><\/code> and a new tag with semantic version format <code>(v.0.0.1<\/code>) is created. <\/p>\n\n\n\n<p>Release notes are added and the repository is tagged, then bundled as a tarball (in this case). The python package is rebuilt using the v0.0.1 tag as the version and then deployed to the Development environment followed by Production. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"625\" src=\"https:\/\/platformdev.org\/wp-content\/uploads\/2024\/09\/image-1024x625.png\" alt=\"\" class=\"wp-image-194\" srcset=\"https:\/\/platformdev.org\/wp-content\/uploads\/2024\/09\/image-1024x625.png 1024w, https:\/\/platformdev.org\/wp-content\/uploads\/2024\/09\/image-300x183.png 300w, https:\/\/platformdev.org\/wp-content\/uploads\/2024\/09\/image-768x469.png 768w, https:\/\/platformdev.org\/wp-content\/uploads\/2024\/09\/image.png 1263w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>For more information, including some sample workflows please see the link below. <\/p>\n\n\n\n<p>Sample Reusable Workflows:<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/platformdevorg\/shared-workflows\">https:\/\/github.com\/platformdevorg\/shared-workflows<\/a><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When developing projects in python, it&#8217;s important to have a reliable CI workflow that can perform the following functions. Before we dive into these steps, let&#8217;s briefly discuss our branching \/ release strategy. The most common strategies are GitFlow, Github flow, Trunk based, Gitlab flow, and variations of each. For the majority of my projects [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":137,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"site-container-style":"default","site-container-layout":"default","site-sidebar-layout":"default","site-transparent-header":"default","disable-article-header":"default","disable-site-header":"default","disable-site-footer":"default","disable-content-area-spacing":"default","footnotes":""},"categories":[1],"tags":[11,10,12],"_links":{"self":[{"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/posts\/159"}],"collection":[{"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/comments?post=159"}],"version-history":[{"count":8,"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/posts\/159\/revisions"}],"predecessor-version":[{"id":202,"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/posts\/159\/revisions\/202"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/media\/137"}],"wp:attachment":[{"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/media?parent=159"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/categories?post=159"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/platformdev.org\/index.php\/wp-json\/wp\/v2\/tags?post=159"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}