Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions .flake8

This file was deleted.

23 changes: 23 additions & 0 deletions .github/scripts/bump_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from tomlkit import parse, dumps


def bump_patch(version: str) -> str:
major, minor, patch = map(int, version.split("."))
patch += 1
return f"{major}.{minor}.{patch}"


# --- read TOML ---
with open("pyproject.toml", "r") as f:
pyproject = parse(f.read())

# --- bump version ---
current_version = pyproject["project"]["version"]
new_version = bump_patch(current_version)
pyproject["project"]["version"] = new_version

# --- write back ---
with open("pyproject.toml", "w") as f:
f.write(dumps(pyproject))

print(f"Version bumped: {current_version} → {new_version}")
74 changes: 74 additions & 0 deletions .github/scripts/check_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import toml
import re
import subprocess
import sys


def compare_versions(new_version: str, current_version: str) -> int:
"""
Compare two versions.
:param new_version: The version from the PR
:param current_version: The version that is currently in the main branch
Returns:
1 if new_version > current_version
-1 if new_version < current_version
0 if equal
"""
pr_parts = list(map(int, new_version.split(".")))
main_parts = list(map(int, current_version.split(".")))

if pr_parts > main_parts:
return 1
elif pr_parts < main_parts:
return -1
else:
return 0


# --- PR version ---
with open("pyproject.toml", "r") as file:
pyproject = toml.load(file)
pr_version = pyproject["project"]["version"]

print(f"PR Version: {pr_version}")

# --- Fetch main branch ---
try:
subprocess.run(
["git", "fetch", "origin", "main"],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
except subprocess.CalledProcessError:
print("⚠️ Could not fetch origin/main. Skipping version comparison.")
sys.exit(0)

# --- Main branch version (without checkout) ---
try:
git_show = subprocess.run(
["git", "show", "origin/main:pyproject.toml"],
check=True,
text=True,
capture_output=True,
)
main_branch_pyproject = toml.loads(git_show.stdout)
main_version = main_branch_pyproject["project"]["version"]
except subprocess.CalledProcessError:
print("⚠️ No pyproject.toml found on origin/main. Skipping version comparison.")
sys.exit(0)

print(f"Main Branch Version: {main_version}")

# --- Validate version format ---
version_regex = r"^\d+\.\d+\.\d+$"
if not re.match(version_regex, pr_version) or not re.match(version_regex, main_version):
raise ValueError("One of the versions does not match the expected format (major.minor.patch).")

# --- Compare ---
comparison_result = compare_versions(pr_version, main_version)

if comparison_result <= 0:
raise ValueError(f"PR version ({pr_version}) must be greater than the main branch version ({main_version}).")
else:
print(f"✅ PR version {pr_version} is greater than the main branch version {main_version}.")
64 changes: 20 additions & 44 deletions .github/workflows/version-number.yml
Original file line number Diff line number Diff line change
@@ -1,48 +1,24 @@
name: Version
name: Check Pyproject Version

on:
pull_request:
branches:
- '*'
pull_request:
branches:
- '*'

jobs:
build:
name: Checking Version Number
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Read version contents
id: new_version
run: |
echo "NEW_VERSION=$(cat .version)" >> $GITHUB_ENV

- name: Print Version
run: |
echo ${{ env.NEW_VERSION }}

- name: Checkout to main
uses: actions/checkout@v2
with:
ref: main
fetch-depth: 1

- name: Read main version contents
id: main_version
run: |
echo "MAIN_VERSION=$(cat .version)" >> $GITHUB_ENV


- name: Print main version
run: |
echo ${{ env.MAIN_VERSION }}


- name: Compare versions
run: |
if [ ${{ env.NEW_VERSION }} != ${{ env.MAIN_VERSION }} ]
then
echo Version updated!
else
echo Version not updated!
exit 1
fi
uv-example:
permissions:
contents: read
name: Python
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up UV
uses: astral-sh/setup-uv@v6

- name: Check version in pyproject.toml
run: |
uv run --only-group version-check python .github/scripts/check_version.py
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ app/__pycache__/
tests/__pycache__/
.dccache
venv
.venv
1 change: 0 additions & 1 deletion .version

This file was deleted.

33 changes: 29 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
FROM europe-west2-docker.pkg.dev/ons-sdx-ci/sdx-apps/sdx-gcp:1.4.4
COPY . /app
# Use a Python image with uv pre-installed
FROM ghcr.io/astral-sh/uv:python3.13-alpine

# Install the project into `/app`
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["python", "./run.py"]

# Enable bytecode compilation
ENV UV_COMPILE_BYTECODE=1

# Copy project config and lockfile first (for better caching)
COPY pyproject.toml uv.lock ./

# Place executables in the environment at the front of the path
ENV PATH="/app/.venv/bin:$PATH"

# Then, add the rest of the project source code and install it
# Installing separately from its dependencies allows optimal layer caching
ADD . /app

# Install dependencies
RUN uv sync --frozen --no-dev

# Expose the port the app runs on
EXPOSE 5000

# Reset the entrypoint to avoid potentially prefixing the command from other based images.
# i.e ENTRYPOINT ["python"] + CMD ["python", "run.py"] will result in ENTRYPOINT ["python", "python", "run.py"]
ENTRYPOINT []

CMD ["uv", "run", "--no-dev", "run.py"]
36 changes: 28 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,32 @@ SHELL := bash
.ONESHELL:


PHONY: install
install: ## Install dependencies
uv sync


.PHONY: test
test:
. venv/bin/activate \
&& python3 --version \
&& python3 -m pip install --upgrade pip \
&& pip install -r requirements.txt \
&& pip install -r test-requirements.txt \
&& flake8 . --count --statistics \
&& pytest -v --cov-report term-missing --disable-warnings --cov=app tests/
test: install
@echo "Running tests..."
uv run pytest -v --cov-report term-missing --disable-warnings --cov=app tests/


.PHONY: lint
lint:
@echo "Running Ruff linter..."
uv run --only-group lint ruff check --fix


.PHONY: dev
dev:
@echo "Starting development server..."
uv run run.py


.PHONY: bump
bump:
@echo "🔼 Bumping project version..."
uv run --only-group version-check python .github/scripts/bump_version.py
@echo "🔄 Generating new lock file..."
uv lock
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# sdx-cleanup

![Version](https://ons-badges-752336435892.europe-west2.run.app/api/badge/custom?left=Python&right=3.13)

The SDX-Cleanup service is used within the Office National of Statistics (ONS) for removing
any artefacts that remain after successful ingestion by NIFI - indicated by the posting
of a receipt on the dap-receipt-topic.
Expand Down
30 changes: 4 additions & 26 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,6 @@
import os
import logging
from logging import Logger

from sdx_gcp.app import get_logger, SdxApp

logger = get_logger()

project_id = os.getenv('PROJECT_ID', 'ons-sdx-sandbox')


class Config:
"""class to hold required configuration data"""

def __init__(self, proj_id) -> None:
self.PROJECT_ID = proj_id

self.OUTPUT_BUCKET_NAME = f'{proj_id}-outputs'
self.SEFT_INPUT_BUCKET_NAME = f'{proj_id}-seft-responses'
self.SURVEY_INPUT_BUCKET_NAME = f'{proj_id}-survey-responses'

self.RECEIPT_SUBSCRIPTION_ID = "dap-receipt-subscription"
self.QUARANTINE_TOPIC_ID = "cleanup-failure-topic"

self.COMMENT_EXPIRY_IN_DAYS = 90


CONFIG = Config(project_id)

sdx_app = SdxApp("sdx-cleanup", project_id)
def get_logger() -> Logger:
return logging.getLogger("sdx-cleanup")
Loading
Loading