From dcc942c508a197d5ff1c928c3f1dcba8a6cad22b Mon Sep 17 00:00:00 2001 From: Grzegorz Kostkowski Date: Mon, 6 Jul 2026 16:26:34 +0200 Subject: [PATCH 1/9] Update the CHANGELOG to describe the fixes and improvements made for the D4 redelivery --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aad6ffa..40f1ff6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.0.0-rc.2] - 2026-06-30 + +### Changed +* ERSys installation instructions and related documentation improved (TEDSWS-520) +* Meaningfy-specific references removed from the source code repositories (TEDSWS-528) + + ## [1.1.0-rc.4] - 2026-06-30 ### Changed From 14fbeadbb2a0cc1f7a83567da19f05b192025c00 Mon Sep 17 00:00:00 2001 From: Grzegorz Kostkowski Date: Wed, 8 Jul 2026 09:40:32 +0200 Subject: [PATCH 2/9] chore(logging): disable traceback for controlled exceptions --- src/ere/services/entity_resolution_service.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ere/services/entity_resolution_service.py b/src/ere/services/entity_resolution_service.py index dbc5e36..d013f8a 100644 --- a/src/ere/services/entity_resolution_service.py +++ b/src/ere/services/entity_resolution_service.py @@ -489,9 +489,10 @@ def process_request(self, request: ERERequest) -> EREResponse: timestamp=now, ) except Exception as exc: # pylint: disable=broad-exception-caught - log.exception( - "Resolution error for mention %s", + log.error( + "Resolution error for mention %s: %s", request.ere_request_id, + exc, ) return EREErrorResponse( ere_request_id=request.ere_request_id, From c92dece2467c19f032f66b4849f9ac12d79d0d11 Mon Sep 17 00:00:00 2001 From: Grzegorz Kostkowski Date: Wed, 8 Jul 2026 09:47:28 +0200 Subject: [PATCH 3/9] chore(logging): add NOSONAR suppression for intentional no-traceback log --- src/ere/services/entity_resolution_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ere/services/entity_resolution_service.py b/src/ere/services/entity_resolution_service.py index d013f8a..bbeeac2 100644 --- a/src/ere/services/entity_resolution_service.py +++ b/src/ere/services/entity_resolution_service.py @@ -489,7 +489,7 @@ def process_request(self, request: ERERequest) -> EREResponse: timestamp=now, ) except Exception as exc: # pylint: disable=broad-exception-caught - log.error( + log.error( # NOSONAR - intentionally no traceback; service converts to EREErrorResponse "Resolution error for mention %s: %s", request.ere_request_id, exc, From 88b97f9cbac024c7c4516e7adb1ea7cd9f2c2bad Mon Sep 17 00:00:00 2001 From: Grzegorz Kostkowski Date: Wed, 8 Jul 2026 12:02:12 +0200 Subject: [PATCH 4/9] fix(services): two-tier exception handling in process_request Controlled exceptions (ValueError, ConflictError) are logged with log.error (no traceback, typed response). Uncontrolled exceptions are caught as fallback with log.exception (full traceback, generic InternalError response). Adds top-level ConflictError import. --- src/ere/services/entity_resolution_service.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ere/services/entity_resolution_service.py b/src/ere/services/entity_resolution_service.py index bbeeac2..d5a05cd 100644 --- a/src/ere/services/entity_resolution_service.py +++ b/src/ere/services/entity_resolution_service.py @@ -20,6 +20,7 @@ MentionRepository, SimilarityRepository, ) +from ere.models.exceptions import ConflictError from ere.models.resolver import ( CandidateCluster, ClusterId, @@ -229,8 +230,6 @@ def check_conflict(self, mention: Mention) -> None: Raises: ConflictError: If mention_id already exists with different attributes. """ - from ere.models.exceptions import ConflictError - existing = self._mention_repo.find_by_id(mention.id) if existing is not None and existing.attributes != mention.attributes: raise ConflictError( @@ -488,8 +487,8 @@ def process_request(self, request: ERERequest) -> EREResponse: ere_request_id=request.ere_request_id, timestamp=now, ) - except Exception as exc: # pylint: disable=broad-exception-caught - log.error( # NOSONAR - intentionally no traceback; service converts to EREErrorResponse + except (ValueError, ConflictError) as exc: + log.error( # NOSONAR - intentionally no traceback for controlled exceptions "Resolution error for mention %s: %s", request.ere_request_id, exc, @@ -501,6 +500,18 @@ def process_request(self, request: ERERequest) -> EREResponse: error_detail=str(exc), timestamp=now, ) + except Exception: # pylint: disable=broad-exception-caught + log.exception( + "Unexpected error for mention %s", + request.ere_request_id, + ) + return EREErrorResponse( + ere_request_id=request.ere_request_id, + error_type="InternalError", + error_title="Unexpected error", + error_detail="An unexpected error occurred", + timestamp=now, + ) def __call__(self, request: ERERequest) -> EREResponse: """Make the service callable.""" From c7a932be8f11e0bf5047d7fb6e93ecae7719e7e1 Mon Sep 17 00:00:00 2001 From: Grzegorz Kostkowski Date: Wed, 15 Jul 2026 19:35:13 +0200 Subject: [PATCH 5/9] chore(ci): remove SonarCloud and contractor-specific staging deploy trigger Drop the SonarCloud scan job along with sonar-project.properties, and remove the trigger-staging-deploy job that referenced the contractor ops repo. Bump version to 1.1.0-rc.6 and update the CHANGELOG. --- .github/workflows/ci.yaml | 44 +----------- CHANGELOG.md | 7 +- sonar-project.properties | 140 -------------------------------------- src/VERSION | 2 +- 4 files changed, 9 insertions(+), 184 deletions(-) delete mode 100644 sonar-project.properties diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 105b9ce..c8758a4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,13 +3,9 @@ # Runs on push to develop and on PRs targeting develop. # # Jobs: -# 1. quality — Install, lint, test & verify (tox), SonarCloud -# 2. trigger-staging-deploy — on push to develop only, triggers the -# Deploy ERSys Staging workflow on enity-resolution-ops via the -# GitHub workflow_dispatch API +# 1. quality — Install, lint, test & verify (tox) # # Required secrets: -# - SONAR_TOKEN: SonarCloud authentication token # - CI_GH_TOKEN: org-level PAT for cross-repo workflow dispatch name: CI @@ -45,7 +41,7 @@ jobs: # ------------------------------------------------------------------ - uses: actions/checkout@v6 with: - fetch-depth: 0 # Full history required for SonarCloud analysis + fetch-depth: 0 # ------------------------------------------------------------------ # Python & Poetry @@ -99,39 +95,3 @@ jobs: REDIS_HOST: localhost REDIS_PORT: 6379 REDIS_PASSWORD: "" - - # ------------------------------------------------------------------ - # SonarCloud - # ------------------------------------------------------------------ - - name: Check for SonarCloud Token - id: sonar_check - run: | - if [ -z "${{ secrets.SONAR_TOKEN }}" ]; then - echo "has_token=false" >> $GITHUB_OUTPUT - else - echo "has_token=true" >> $GITHUB_OUTPUT - fi - - - name: SonarCloud scan - if: always() && steps.sonar_check.outputs.has_token == 'true' - uses: SonarSource/sonarqube-scan-action@v6 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - trigger-staging-deploy: - name: Trigger staging deploy - needs: quality - if: github.event_name == 'push' && github.repository_owner == 'meaningfy-ws' - runs-on: ubuntu-latest - env: - OPS_REPO: meaningfy-ws/enity-resolution-ops - DEPLOY_WORKFLOW: deploy-staging.yml - DEPLOY_REF: develop - steps: - - name: Trigger deploy workflow on ops repo - run: | - curl -sf -X POST \ - -H "Authorization: token ${{ secrets.CI_GH_TOKEN }}" \ - -H "Accept: application/vnd.github.v3+json" \ - "https://api.github.com/repos/${OPS_REPO}/actions/workflows/${DEPLOY_WORKFLOW}/dispatches" \ - -d '{"ref":"${{ env.DEPLOY_REF }}","inputs":{"repo":"${{ github.repository }}","sha":"${{ github.sha }}"}}' diff --git a/CHANGELOG.md b/CHANGELOG.md index 40f1ff6..47ccfea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.1.0-rc.6] - 2026-07-16 + +### Removed +* SonarCloud integration, as it depended on contractor-specific configuration (TEDSWS-528). + ## [1.0.0-rc.2] - 2026-06-30 ### Changed * ERSys installation instructions and related documentation improved (TEDSWS-520) -* Meaningfy-specific references removed from the source code repositories (TEDSWS-528) +* Contractor-specific references removed from the source code repositories (TEDSWS-528) ## [1.1.0-rc.4] - 2026-06-30 diff --git a/sonar-project.properties b/sonar-project.properties deleted file mode 100644 index 0a21d94..0000000 --- a/sonar-project.properties +++ /dev/null @@ -1,140 +0,0 @@ -# SonarCloud Configuration for Entity Resolution Engine (ERE) -# =========================================================== -# This file configures quality gates and analysis for SonarCloud. -# See: https://docs.sonarcloud.io/ - -#----------------------------------------------------------------------------- -# Project Identity -#----------------------------------------------------------------------------- - -# Must be unique in SonarCloud instance -sonar.projectKey=meaningfy-ws_entity-resolution-engine-basic -sonar.organization=meaningfy-ws - -# Display name and version -sonar.projectName=Entity Resolution Engine (ERE) -sonar.projectVersion=1.0.0 - -#----------------------------------------------------------------------------- -# Code Analysis Paths -#----------------------------------------------------------------------------- - -# Source code location (relative to sonar-project.properties) -sonar.sources=src/ere -sonar.tests=test - -# Source encoding -sonar.sourceEncoding=UTF-8 - -# Python version (must match pyproject.toml requires-python) -sonar.python.version=3.12 - -#----------------------------------------------------------------------------- -# Coverage & Test Report Paths -#----------------------------------------------------------------------------- - -# Coverage report (generated by pytest-cov via tox) -sonar.python.coverage.reportPaths=coverage.xml - -# Test results report (if using XML format) -sonar.python.xunit.reportPath=test-results.xml - -# Pylint report (optional, for additional insights) -sonar.python.pylint.reportPath=pylint-report.txt - -#----------------------------------------------------------------------------- -# Exclusions (don't analyze these) -#----------------------------------------------------------------------------- - -# Exclude test files from coverage metrics -sonar.coverage.exclusions=test/**/*,setup.py,**/__init__.py - -# Exclude test files from duplication detection -sonar.cpd.exclusions=test/**/* - -# Exclude documentation and config files from analysis -sonar.exclusions=docs/**/*,*.md,src/infra/**/*,src/demo/**/* - -#----------------------------------------------------------------------------- -# SOLID Principles & Clean Code Quality Gates -#----------------------------------------------------------------------------- - -# Single Responsibility Principle (SRP) -# Cognitive complexity per function (max 15 is recommended) -sonar.python.S3776.threshold=15 - -# Cyclomatic complexity per function (max 10 is good practice) -sonar.python.S1541.threshold=10 - -# Max lines per function (enforce small, focused functions) -sonar.python.S104.max=50 - -# Max lines per class (enforce focused classes) -sonar.python.S1188.max=500 - -# Open/Closed Principle (OCP) -# Discourage too many conditional statements (suggests need for polymorphism) -sonar.python.S1066.max=3 - -# Liskov Substitution Principle (LSP) -# Limit inheritance depth to avoid deep hierarchies -sonar.python.S110.max=4 - -# Don't Repeat Yourself (DRY) - Related to SOLID -# Minimum duplicate lines to trigger an issue -sonar.cpd.python.minimumLines=3 - -# Minimum duplicate tokens -sonar.cpd.python.minimumTokens=50 - -#----------------------------------------------------------------------------- -# Quality Gate Configuration -# -# Note: These conditions must also be configured in SonarCloud UI under: -# Organization Settings → Quality Gates → Create "ERE SOLID Gate" -# -# Create custom quality gate with these conditions on New Code: -# - Critical Issues: is greater than 0 → FAIL -# - Blocker Issues: is greater than 0 → FAIL -# - Security Hotspots Reviewed: is less than 100% → FAIL -# - Coverage: is less than 80% → FAIL -# - Duplicated Lines: is greater than 3% → FAIL -# - Code Smells: is greater than 0 → FAIL (optional) -# -#----------------------------------------------------------------------------- - -# Wait for quality gate result (useful in CI) -sonar.qualitygate.wait=true -sonar.qualitygate.timeout=300 - -# Branch analysis configuration -sonar.branch.autoconfig.disabled=false - -#----------------------------------------------------------------------------- -# Maintainability & Code Quality Thresholds -# -# Rating scale: -# A = tech debt ratio <= 5% -# B = tech debt ratio 6-10% -# C = tech debt ratio 11-20% -# -#----------------------------------------------------------------------------- - -# Enforce at least B rating -sonar.maintainability.rating.threshold=B - -# New code should have zero code smells (potential issues) -sonar.newCode.codeSmells=0 - -# Overall complexity threshold for functions -sonar.complexity.threshold=10 - -#----------------------------------------------------------------------------- -# Reporting -#----------------------------------------------------------------------------- - -# Generate reports for historical tracking -sonar.report.export.path=sonar-report - -# Verbose logging (helpful for debugging) -sonar.verbose=false diff --git a/src/VERSION b/src/VERSION index 61cb4de..812896c 100644 --- a/src/VERSION +++ b/src/VERSION @@ -1 +1 @@ -1.1.0-rc.4 +1.1.0-rc.6 From 3657b407295974e5c915694ba26b2370dfe642f2 Mon Sep 17 00:00:00 2001 From: Grzegorz Kostkowski Date: Wed, 15 Jul 2026 21:13:24 +0200 Subject: [PATCH 6/9] chore(ci): clean up obsolete comment --- .github/workflows/ci.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c8758a4..e10f96b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,9 +4,7 @@ # # Jobs: # 1. quality — Install, lint, test & verify (tox) -# -# Required secrets: -# - CI_GH_TOKEN: org-level PAT for cross-repo workflow dispatch + name: CI From 281cc8768b9cf1e897fad0a5257fedc24c402a17 Mon Sep 17 00:00:00 2001 From: Grzegorz Kostkowski Date: Wed, 15 Jul 2026 21:24:51 +0200 Subject: [PATCH 7/9] docs: update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47ccfea..992fbb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] + ## [1.1.0-rc.6] - 2026-07-16 +### Changed +* Disable logging of error traceback for controlled exceptions; the traceback logging is retained for uncontrolled exceptions (TEDSWS-529, TEDSWS-534) + ### Removed * SonarCloud integration, as it depended on contractor-specific configuration (TEDSWS-528). From 742d2e5ed52307f8db26b325fa124284f82b7d63 Mon Sep 17 00:00:00 2001 From: Rashif Ray Rahman Date: Thu, 16 Jul 2026 14:55:41 +0600 Subject: [PATCH 8/9] fix(docker): ensure app files are owned by appuser Add `--chown=appuser:appuser` to COPY commands in runtime stage. Without explicit ownership, files inherit `root:root` with source permissions -- breaks when built with restrictive umask (027/077). --- src/infra/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/infra/Dockerfile b/src/infra/Dockerfile index cf92f40..7f4ae02 100644 --- a/src/infra/Dockerfile +++ b/src/infra/Dockerfile @@ -69,10 +69,10 @@ RUN groupadd --gid 1000 appuser && \ WORKDIR /app -# Copy only the virtual environment and necessary app files -COPY --from=builder /app/.venv /app/.venv -COPY --from=builder /app/ere /app/ere -COPY --from=builder /app/config /app/config +# Copy only the virtual environment and necessary app files — chown to appuser so non-root process can read +COPY --from=builder --chown=appuser:appuser /app/.venv /app/.venv +COPY --from=builder --chown=appuser:appuser /app/ere /app/ere +COPY --from=builder --chown=appuser:appuser /app/config /app/config # Volume mount point for DuckDB persistent storage RUN mkdir -p /data && chown appuser:appuser /data From 2e514484aa66498efc58fd6bbf788a8c283d1873 Mon Sep 17 00:00:00 2001 From: Rashif Ray Rahman Date: Thu, 16 Jul 2026 16:56:23 +0600 Subject: [PATCH 9/9] chore: Normalize line ending for rdf_mapping.yaml test resource There appear to be many files with CRLF line endings, but this particular one was problematic as it would be interpreted as modified by Git perpetually, for anyone who checks it out with no `autocrlf` setting. As a result, Git would forbid switching branches, and downstream tools will continue to interpret that there is a change. ```sh $ git ls-files --eol | grep rdf_mapping.yaml i/lf w/lf attr/text eol=lf src/config/rdf_mapping.yaml i/crlf w/crlf attr/text eol=lf test/resources/rdf_mapping.yaml ``` This is because the file was committed as CRLF despite there being an enforcement of UNIX EOL via `.gitattributes`. Both Git internal and working copies are CRLF in this case. The fix is to renormalize and commit the file: ```sh git add --renormalize . git commit -m "Normalize line endings" ``` And then reinitialize the local copy: ```sh rm test/resources/rdf_mapping.yaml git checkout HEAD -- test/resources/rdf_mapping.yaml ``` Verify that the Git-internal (`i`) and working copy (`w`; yours) are now correct: ```sh $ git ls-files --eol | grep rdf_mapping.yaml i/lf w/lf attr/text eol=lf src/config/rdf_mapping.yaml i/lf w/lf attr/text eol=lf test/resources/rdf_mapping.yaml ``` --- test/resources/rdf_mapping.yaml | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/resources/rdf_mapping.yaml b/test/resources/rdf_mapping.yaml index 8dd02e2..4b856ed 100644 --- a/test/resources/rdf_mapping.yaml +++ b/test/resources/rdf_mapping.yaml @@ -1,20 +1,20 @@ -# Namespace prefix registry - used by rdf_mapper.py to resolve prefixed names in field paths -namespaces: - epo: "http://data.europa.eu/a4g/ontology#" - org: "http://www.w3.org/ns/org#" - locn: "http://www.w3.org/ns/locn#" - cccev: "http://data.europa.eu/m8g/" - -# Entity type mappings: entity_type_string -> rdf_type + field property paths -# Property paths use / as separator for multi-hop traversal. -# Field names must match entity_fields in resolver.yaml (legal_name, country_code). -entity_types: - ORGANISATION: - rdf_type: "org:Organization" - fields: - legal_name: "epo:hasLegalName" - country_code: "cccev:registeredAddress/epo:hasCountryCode" - nuts_code: "cccev:registeredAddress/epo:hasNutsCode" - post_code: "cccev:registeredAddress/locn:postCode" - post_name: "cccev:registeredAddress/locn:postName" - thoroughfare: "cccev:registeredAddress/locn:thoroughfare" +# Namespace prefix registry - used by rdf_mapper.py to resolve prefixed names in field paths +namespaces: + epo: "http://data.europa.eu/a4g/ontology#" + org: "http://www.w3.org/ns/org#" + locn: "http://www.w3.org/ns/locn#" + cccev: "http://data.europa.eu/m8g/" + +# Entity type mappings: entity_type_string -> rdf_type + field property paths +# Property paths use / as separator for multi-hop traversal. +# Field names must match entity_fields in resolver.yaml (legal_name, country_code). +entity_types: + ORGANISATION: + rdf_type: "org:Organization" + fields: + legal_name: "epo:hasLegalName" + country_code: "cccev:registeredAddress/epo:hasCountryCode" + nuts_code: "cccev:registeredAddress/epo:hasNutsCode" + post_code: "cccev:registeredAddress/locn:postCode" + post_name: "cccev:registeredAddress/locn:postName" + thoroughfare: "cccev:registeredAddress/locn:thoroughfare"