Add continuous integration pipeline
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

Added basic continuous integration tests to run on any push:
On main branch, the python program is built.
On tagged commit, a gitea release is created.

Fixed first detected build pipeline issues:
Fixing mypy library stubs missing for some imported libraries.
Fixed two small typing errors for Repetitions.

The steps run on basic python containers, onto which the ci steps simply
install poetry.
This takes a little more processing time during pipeline running (~16s
per step),
but also gives a lot of flexibility in container usage.

Added script which assists in creating an automatic release by
extracting the current version and newest changes from the semantic
changelog.
This is then used in the gitea release preparation as title and
content of the release message.
The files built in the dist directory by poetry will be attached.
This commit is contained in:
Marty Oehme 2022-01-18 18:08:24 +01:00
parent b0f8c48e99
commit 03dd1a485d
Signed by: Marty
GPG key ID: B7538B8F50A1C800
6 changed files with 148 additions and 4 deletions

75
.woodpecker.yml Normal file
View file

@ -0,0 +1,75 @@
branches: main
pipeline:
code_lint:
image: python
commands:
- pip install poetry
- poetry install
- pip install black
- echo "----------------- running lint ------------------"
- python --version && poetry --version && black --version
- poetry run black .
unit_tests:
image: thekevjames/nox
commands:
- pip install poetry
- poetry install
- echo "----------------- running tests ------------------"
- python --version && poetry --version && nox --version
- poetry run nox
static_analysis:
image: python
commands:
- pip install poetry
- poetry install
- pip install mypy
- echo "----------------- running analysis ------------------"
- python --version && poetry --version && mypy --version
- poetry run mypy .
build_dist:
image: python
commands:
- pip install poetry
- poetry install
- echo "----------------- running analysis ------------------"
- python --version && poetry --version
- poetry build
when:
branch: main
release_prep:
image: python
commands:
- echo "----------------- preparing release ------------------"
- python tools/extract-changelog.py
gitea_release:
image: plugins/gitea-release
settings:
api_key:
from_secret: gitea_release_token
base_url: https://git.martyoeh.me
files: dist/*
title: NEWEST_VERSION.md
note: NEWEST_CHANGES.md
when:
event: tag
tag: v*
notify_matrix:
image: plugins/matrix
settings:
homeserver: https://matrix.org
roomid:
from_secret: matrix_roomid
userid:
from_secret: matrix_userid
accesstoken:
from_secret: matrix_token
when:
status: [ success, failure ]

View file

@ -17,7 +17,7 @@ and this project tries to adhere to [Semantic Versioning](https://semver.org/spe
* Compatible with Python stretching back to version 3.7 * Compatible with Python stretching back to version 3.7
## [0.4] - 2021-12-06 ## [0.4.0] - 2021-12-06
### Added ### Added

View file

@ -38,3 +38,13 @@ show_missing = true
[build-system] [build-system]
requires = ["poetry-core>=1.0.0"] requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[[tool.mypy.overrides]]
module = [
"click",
"click.testing",
"pytest",
"nox",
"importlib-metadata"
]
ignore_missing_imports = true

View file

@ -4,6 +4,6 @@ import sys
try: try:
from importlib.metadata import version as metadata_version from importlib.metadata import version as metadata_version
except ImportError: except ImportError:
from importlib_metadata import version as metadata_version from importlib_metadata import version as metadata_version # type: ignore
__version__ = str(metadata_version(__name__)) __version__ = str(metadata_version(__name__))

View file

@ -55,10 +55,11 @@ def habit_list_add_ids(c: sqlite3.Cursor, habitlist: list[Habit]) -> dict[int, H
:return habit_id_dict: The habit collection as a dict with the keys :return habit_id_dict: The habit collection as a dict with the keys
consisting of the habit's sqlite database ID. consisting of the habit's sqlite database ID.
""" """
with_id = {} with_id: dict[int, Habit] = {}
for h in habitlist: for h in habitlist:
sql_id = fetch_habit_id(c, h.uuid or "") sql_id = fetch_habit_id(c, h.uuid or "")
with_id[sql_id] = h if sql_id is not None:
with_id[sql_id] = h
return with_id return with_id
@ -74,6 +75,8 @@ def fetch_habit_id(cursor: sqlite3.Cursor, uuid: str) -> Optional[int]:
if id is not None: if id is not None:
return id[0] return id[0]
return None
def add_to_database( def add_to_database(
cursor: sqlite3.Cursor, habits: dict[int, Habit], repetition: Repetition cursor: sqlite3.Cursor, habits: dict[int, Habit], repetition: Repetition

View file

@ -0,0 +1,56 @@
import re
## Extracts the version and newest changes from a semantic changelog.
#
# Important, it only works with three-parted version numbers
# a-la 1.2.3 or 313.01.1888 -- needs \d.\d.\d to work.
#
# The version number and changeset will be put in `NEWEST_VERSION.md`
# and `NEWEST_CHANGES.md` respectively, for further use in releases.
OUTPUT_FILE_VERSION = "NEWEST_VERSION.md"
OUTPUT_FILE_CHANGES = "NEWEST_CHANGES.md"
def getVersion(file):
for line in file:
m = re.match(r"^## \[(\d+\.\d+\.\d+)\]", line)
if m and m.group(1):
return m.group(1)
def getSection(file):
inRecordingMode = False
for line in file:
if not inRecordingMode:
if re.match(r"^## \[\d+\.\d+\.\d+\]", line):
inRecordingMode = True
elif re.match(r"^## \[\d+\.\d+\.\d+\]", line):
inRecordingMode = False
break
elif re.match(r"^$", line):
pass
else:
yield line
def toFile(fname, content):
file = open(fname, "w")
file.write(content)
file.close()
with open("CHANGELOG.md") as file:
title = getVersion(file)
print(title)
toFile(OUTPUT_FILE_VERSION, title)
with open("CHANGELOG.md") as file:
newest_changes_gen = getSection(file)
newest_changes = ""
for line in newest_changes_gen:
newest_changes += line
print("[Extracted Changelog]")
print(newest_changes)
toFile(OUTPUT_FILE_CHANGES, newest_changes)
file.close()