Skip to content

Commit

Permalink
add CI/CD and initial tests
Browse files Browse the repository at this point in the history
  • Loading branch information
relud committed Aug 5, 2024
1 parent cc96863 commit 14d7620
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 4 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Test python package

on:
push:
tags:
- v20[0-9][0-9].[01][0-9].[0-3][0-9] # e.g. v2023.12.04
- v20[0-9][0-9].[01][0-9].[0-3][0-9]-[0-9] # e.g. v2023.12.04-2

jobs:
release:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"]

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m venv venv
venv/bin/pip install -r requirements.txt
- name: Build wheel
run: venv/bin/python -m build
- name: Publish release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: >
gh release create ${{ github.ref_name }}
--repo="$GITHUB_REPOSITORY"
--generate-notes
dist/*.whl
67 changes: 67 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Test python package

on:
push:
branches:
- main
pull_request:
branches:
- main

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

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m venv venv
venv/bin/pip install -r requirements.txt
- name: Verify requirements.txt
if: ${{ matrix.python-version == '3.11' }}
run: |
venv/bin/pip-compile --quiet --allow-unsafe --generate-hashes --strip-extras
git diff --exit-code -- requirements.txt
- name: Run lint check
if: ${{ matrix.python-version == '3.11' }}
run: |
venv/bin/ruff format --check obs_common tests
venv/bin/ruff check obs_common tests
- name: Run tests
run: |
venv/bin/pytest tests/
- name: License Check
run: |
venv/bin/pip install -e .
venv/bin/license-check
trigger-release:
if: ${{ github.ref == 'refs/heads/main' }}
needs: test
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m venv venv
venv/bin/pip install -r requirements.txt
- name: Auto-tag a release
run: |
TAG="$(venv/bin/python -c 'import obs_common.release; print(obs_common.release.generate_tag())')"
git tag -s "$TAG" -m "Tag $TAG"
git push --tags
5 changes: 2 additions & 3 deletions obs_common/license_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,15 @@ def has_license_header(path: pathlib.Path):
return False


def main():
def main(*args):
parser = argparse.ArgumentParser(description=DESCRIPTION)
parser.add_argument(
"-l", "--file-only", action="store_true", help="print files only"
)
parser.add_argument("--verbose", action="store_true", help="verbose output")
parser.add_argument("target", help="file or directory tree to check", nargs="?")

parsed = parser.parse_args()
#parsed = parser.parse_args(args)
parsed = parser.parse_args(*args)

if parsed.target:
target = pathlib.Path(parsed.target)
Expand Down
38 changes: 38 additions & 0 deletions obs_common/release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

import subprocess
from datetime import datetime


def generate_tag():
"""Generate a release tag based on the current date.
If the release tag already exists in git, add a monotonically increasing integer
suffix separated by a dash.
For example:
"v2024.07.01"
"v2024.07.01-1"
"v2024.07.01-2"
"""
base_tag_name = datetime.now().strftime("v%Y.%m.%d")
existing_tags = subprocess.check_output(
["git", "tag", "-l", base_tag_name, f"{base_tag_name}-*"]
)
tag_indices = set()
for tag in existing_tags.decode("utf-8").split("\n"):
if not tag:
continue
if "-" not in tag:
tag_indices.add(0)
try:
tag_indices.add(int(tag.split("-", 1)[-1]))
except ValueError:
continue
last_tag = max(tag_indices, default=None)
if last_tag is None:
return base_tag_name
return f"{base_tag_name}-{last_tag+1}"
12 changes: 11 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
[project]
name = "obs-common"
description = "code shared between obs-team repositories"
license = {file = "LICENSE"}
readme = "README.md"
requires-python = ">=3.11"
classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = []
dynamic = ["version"]

[project.scripts]
Expand Down Expand Up @@ -33,7 +41,9 @@ ignore = ["E501"]
docstring-quotes = "double"

[tool.ruff.lint.per-file-ignores]
"obs_common/license-check.py" = ["S603", "S607"]
"obs_common/license_check.py" = ["S603", "S607"]
"obs_common/release.py" = ["S603", "S607"]
"tests/**/*.py" = ["S101"]


[tool.pytest.ini_options]
Expand Down
3 changes: 3 additions & 0 deletions requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pip-tools
ruff
pytest
76 changes: 76 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile --allow-unsafe --generate-hashes --strip-extras
#
build==1.2.1 \
--hash=sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d \
--hash=sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4
# via pip-tools
click==8.1.7 \
--hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
--hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
# via pip-tools
iniconfig==2.0.0 \
--hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \
--hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374
# via pytest
packaging==24.1 \
--hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \
--hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124
# via
# build
# pytest
pip-tools==7.4.1 \
--hash=sha256:4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9 \
--hash=sha256:864826f5073864450e24dbeeb85ce3920cdfb09848a3d69ebf537b521f14bcc9
# via -r requirements.in
pluggy==1.5.0 \
--hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \
--hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669
# via pytest
pyproject-hooks==1.1.0 \
--hash=sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965 \
--hash=sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2
# via
# build
# pip-tools
pytest==8.3.2 \
--hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \
--hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce
# via -r requirements.in
ruff==0.5.5 \
--hash=sha256:00817603822a3e42b80f7c3298c8269e09f889ee94640cd1fc7f9329788d7bf8 \
--hash=sha256:187a60f555e9f865a2ff2c6984b9afeffa7158ba6e1eab56cb830404c942b0f3 \
--hash=sha256:3191317d967af701f1b73a31ed5788795936e423b7acce82a2b63e26eb3e89d6 \
--hash=sha256:3687d002f911e8a5faf977e619a034d159a8373514a587249cc00f211c67a091 \
--hash=sha256:4ad25dd9c5faac95c8e9efb13e15803cd8bbf7f4600645a60ffe17c73f60779b \
--hash=sha256:50f36d77f52d4c9c2f1361ccbfbd09099a1b2ea5d2b2222c586ab08885cf3445 \
--hash=sha256:605d589ec35d1da9213a9d4d7e7a9c761d90bba78fc8790d1c5e65026c1b9eaf \
--hash=sha256:696f18463b47a94575db635ebb4c178188645636f05e934fdf361b74edf1bb2d \
--hash=sha256:a09b43e02f76ac0145f86a08e045e2ea452066f7ba064fd6b0cdccb486f7c3e7 \
--hash=sha256:ac9dc814e510436e30d0ba535f435a7f3dc97f895f844f5b3f347ec8c228a523 \
--hash=sha256:af9bdf6c389b5add40d89b201425b531e0a5cceb3cfdcc69f04d3d531c6be74f \
--hash=sha256:cab904683bf9e2ecbbe9ff235bfe056f0eba754d0168ad5407832928d579e7ab \
--hash=sha256:cc5516bdb4858d972fbc31d246bdb390eab8df1a26e2353be2dbc0c2d7f5421a \
--hash=sha256:cfd7de17cef6ab559e9f5ab859f0d3296393bc78f69030967ca4d87a541b97a0 \
--hash=sha256:d0b856cb19c60cd40198be5d8d4b556228e3dcd545b4f423d1ad812bfdca5884 \
--hash=sha256:d40a8533ed545390ef8315b8e25c4bb85739b90bd0f3fe1280a29ae364cc55d8 \
--hash=sha256:f70737c157d7edf749bcb952d13854e8f745cec695a01bdc6e29c29c288fc36e \
--hash=sha256:fe26fc46fa8c6e0ae3f47ddccfbb136253c831c3289bba044befe68f467bfb16
# via -r requirements.in
wheel==0.43.0 \
--hash=sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85 \
--hash=sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81
# via pip-tools

# The following packages are considered to be unsafe in a requirements file:
pip==24.2 \
--hash=sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2 \
--hash=sha256:5b5e490b5e9cb275c879595064adce9ebd31b854e3e803740b72f9ccf34a45b8
# via pip-tools
setuptools==72.1.0 \
--hash=sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1 \
--hash=sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec
# via pip-tools
3 changes: 3 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
21 changes: 21 additions & 0 deletions tests/test_license_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

from obs_common import license_check


def test_license_present(tmp_path):
target = tmp_path / "target.py"
target.write_text(
"# This Source Code Form is subject to the terms of the Mozilla Public\n"
"# License, v. 2.0. If a copy of the MPL was not distributed with this\n"
"# file, You can obtain one at https://mozilla.org/MPL/2.0/.\n"
)
assert license_check.main([str(target)]) == 0


def test_license_missing(tmp_path):
target = tmp_path / "target.py"
target.touch()
assert license_check.main([str(target)]) != 0

0 comments on commit 14d7620

Please sign in to comment.