Setting up OpenWeather Report GitHub Actions

Setting up OpenWeather Report GitHub Actions

January 6, 2025

Overview

OpenWeather Report I did my initial release in 2023. Before I start adding more features, I wanted to create some tooling to help with linting, testing and publishing. Using what I learn from Real Python’s article on continuous integration and some other sites in creating my own workflows.

CI/CD

In the past, I used helper scripts and pre-commit to semi-automate linting, testing and publishing. Continuous Integration (CI) and Continuous Deployment (CD) makes it possible to have this process fully automated. Using GitHub Actions, makes this possible at the repository level instead of the developer PC to make the tooling consistent.

For OpenWeather Report, I am the only developer and don’t anticipate getting an additional developers in the near future. However, I would like to understand how these tools work. Keep in mind that Gitea, GitLab and I am sure others also have something similar to GitHub Actions.

Linting

The nice thing about using GitHub Actions, you can separate functions into separate files. Linting is something that I want to happen every time there is a commit at either the main branch or any feature branch. I ended up using uv to install dependencies and to run ruff.

name: Lint Python Code

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install uv
        uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true
          cache-dependency-glob: "uv.lock"
      - uses: actions/setup-python@v5
        with:
          python-version-file: 'pyproject.toml' 

      - name: Install dependencies
        run: uv sync --all-extras --dev
      - name: Run Ruff
        run: uvx ruff check --output-format=github
      - name: Run mypy
        run: uvx mypy -m openweather_report --strict 

This file is placed in .github/workflows/lint.yml. For my purposes running on Ubuntu is fine though GitHub does have other options. Finally, I use mypy to do some static type checking.

Testing

Something I wanted to try is testing multiple versions of Python. Not really required for this project but I would like how to do it before I need to do it.

name: Run Tests

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_call:
  workflow_dispatch:

jobs:
  testing:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12", "3.13"]

    steps:
      - uses: actions/checkout@v4
      - name: Install uv
        uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true
          python-version: ${{ matrix.python-version }}
      - name: Setup python
        run: uv python install
      - name: Install dependencies
        run: uv sync --all-extras --dev
      - name: Run Pytest
        run: pytest tests

This file is placed .github/workflow/testing.yml. For this project will support Python 3.11 through 3.12. Again using uv to install Python and setup dependencies. Finally pytest to perform the tests. Notice the line python-version: ${{ matrix.ptyhon-version }} is where the Python version goes from python-version: ["3.11, "3.12", "3.13"].

Publishing

This program does get packaged and published on PyPi. I also want to create a release on GitHub. Unlike the previous workflows, I only want this to run when a tag is applied.

name: Publish to PyPI
on:
  push:
    tags:
      - '*.*.*'

jobs:
  publish:
    runs-on: ubuntu-latest
    environment: pypi
    permissions:
      id-token: write
      contents: write 
    steps:
      - uses: actions/checkout@v4
      - name: Install uv
        uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true
          cache-dependency-glob: "uv.lock"

      - name: Setup Python
        run: uv python install
      - name: Install dependencies
        run: uv sync --all-extras
      - name: Build package
        run: uv build
      - name: Publish package
        run: uv publish

      - name: Create GitHub Release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release create ${{ github.ref_name }} --title "Release ${{ github.ref_name  }}" -F RELEASE_NOTES.md

This file is placed .github/workflow/publish.yml. The format looks similar to the previous workflow except at the top where when a tag is pushed is the only time this workflow will run. In order to publish to PyPi, I followed the instructions at Publishing for GitHub. Then I use uv install Python and dependencies like before but use uv to build and publish the package.

It took a little bit more work to create a GitHub release. I started with an article by Neo as well as the Real Python and got close but did not like how the release notes looked. Also I was having trouble attempting to put the Python package on GitHub. In the future I would like to include the Python package but for now will do the following:

  • Create a RELEASE_NOTES.md and manually edit for that particular release.
  • Use gh to create the release.
  • For now the release will just be a zip/tarball.

Security

Similar to testing multiple Python versions, for this project I don’t really need do security and dependency updates but I would like to learn how to do it.

---
version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"


  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

This file is located .github/dependabot.yml. This will check my dependencies for my project as well as the other GitHub Actions.

Conclusion

For the most part, it was straight-forward to setup and verify that my GitHub Actions works. For work I will need to use Azure Pipelines. I do have Gitea self hosted instance and would like to be able to do the same thing except not publishing to PyPi but on my own private PyPi repository. Learning how to do GitHub Actions will help with my current projects and extend to my career.