This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git
The following commit(s) were added to refs/heads/main by this push:
new c7cddfb Use a more advanced matcher for binary and source artifact
paths
c7cddfb is described below
commit c7cddfb966e1f101eb85969ff851befaf3223391
Author: Sean B. Palmer <[email protected]>
AuthorDate: Mon Jun 23 17:27:31 2025 +0100
Use a more advanced matcher for binary and source artifact paths
---
atr/routes/projects.py | 23 +--------------------
atr/tasks/checks/__init__.py | 48 ++++++++++++++++++++++++++++----------------
atr/tasks/checks/rat.py | 1 +
poetry.lock | 13 +++++++++++-
pyproject.toml | 1 +
5 files changed, 46 insertions(+), 40 deletions(-)
diff --git a/atr/routes/projects.py b/atr/routes/projects.py
index a86d613..b7d6e57 100644
--- a/atr/routes/projects.py
+++ b/atr/routes/projects.py
@@ -392,28 +392,7 @@ async def _metadata_edit(
def _parse_artifact_paths(artifact_paths: str) -> list[str]:
if not artifact_paths:
return []
- lines = artifact_paths.split("\n")
- # This is similar to announce._download_path_suffix_validated
- paths = []
- for path in lines:
- path = path.strip()
- if not path:
- continue
- if (".." in path) or ("//" in path):
- raise ValueError("Artifact path must not contain .. or //")
- if path.startswith("./"):
- path = path[1:]
- elif path == ".":
- path = "/"
- if not path.startswith("/"):
- path = "/" + path
- # We differ from _download_path_suffix_validated in that we don't add
a trailing slash
- # if not path.endswith("/"):
- # path = path + "/"
- if "/." in path:
- raise ValueError("Artifact path must not contain /.")
- paths.append(path)
- return sorted(paths)
+ return [path.strip() for path in artifact_paths.split("\n") if
path.strip()]
async def _policy_edit(
diff --git a/atr/tasks/checks/__init__.py b/atr/tasks/checks/__init__.py
index 17ed569..7e07d0c 100644
--- a/atr/tasks/checks/__init__.py
+++ b/atr/tasks/checks/__init__.py
@@ -19,11 +19,11 @@ from __future__ import annotations
import dataclasses
import datetime
-import fnmatch
import functools
import pathlib
from typing import TYPE_CHECKING, Any
+import gitignore_parser
import sqlmodel
if TYPE_CHECKING:
@@ -136,11 +136,6 @@ class Recorder:
async def abs_path(self, rel_path: str | None = None) -> pathlib.Path |
None:
"""Construct the absolute path using the required revision."""
- base_dir = util.get_unfinished_dir()
- project_part = self.project_name
- version_part = self.version_name
- revision_part = self.revision_number
-
# Determine the relative path part
rel_path_part: str | None = None
if rel_path is not None:
@@ -148,11 +143,12 @@ class Recorder:
elif self.primary_rel_path is not None:
rel_path_part = self.primary_rel_path
- # Construct the absolute path
- abs_path_parts: list[str | pathlib.Path] = [base_dir, project_part,
version_part, revision_part]
- if isinstance(rel_path_part, str):
- abs_path_parts.append(rel_path_part)
- return pathlib.Path(*abs_path_parts)
+ if rel_path_part is None:
+ return self.abs_path_base()
+ return self.abs_path_base() / rel_path_part
+
+ def abs_path_base(self) -> pathlib.Path:
+ return pathlib.Path(util.get_unfinished_dir(), self.project_name,
self.version_name, self.revision_number)
async def project(self) -> models.Project:
# TODO: Cache project
@@ -167,9 +163,11 @@ class Recorder:
project = await self.project()
if not project.policy_binary_artifact_paths:
return False
- paths = project.policy_binary_artifact_paths
- slash_path = "/" + self.primary_rel_path
- return any(fnmatch.fnmatch(slash_path, path) for path in paths)
+ matches = _create_path_matcher(
+ project.policy_binary_artifact_paths, self.abs_path_base() /
".ignore", self.abs_path_base()
+ )
+ abs_path = await self.abs_path()
+ return matches(str(abs_path))
async def primary_path_is_source(self) -> bool:
if self.primary_rel_path is None:
@@ -177,9 +175,11 @@ class Recorder:
project = await self.project()
if not project.policy_source_artifact_paths:
return False
- paths = project.policy_source_artifact_paths
- slash_path = "/" + self.primary_rel_path
- return any(fnmatch.fnmatch(slash_path, path) for path in paths)
+ matches = _create_path_matcher(
+ project.policy_source_artifact_paths, self.abs_path_base() /
".ignore", self.abs_path_base()
+ )
+ abs_path = await self.abs_path()
+ return matches(str(abs_path))
async def clear(self, primary_rel_path: str | None = None,
member_rel_path: str | None = None) -> None:
async with db.session() as data:
@@ -254,3 +254,17 @@ def with_model(cls: type[schema.Strict]) ->
Callable[[Callable[..., Any]], Calla
return wrapper
return decorator
+
+
+def _create_path_matcher(lines: list[str], full_path: pathlib.Path, base_dir:
pathlib.Path) -> Callable[[str], bool]:
+ rules = []
+ negation = False
+ for line_no, line in enumerate(lines, start=1):
+ rule = gitignore_parser.rule_from_pattern(line.rstrip("\n"),
base_path=base_dir, source=(full_path, line_no))
+ if rule:
+ rules.append(rule)
+ if rule.negation:
+ negation = True
+ if not negation:
+ return lambda file_path: any(r.match(file_path) for r in rules)
+ return lambda file_path: gitignore_parser.handle_negation(file_path, rules)
diff --git a/atr/tasks/checks/rat.py b/atr/tasks/checks/rat.py
index ec6d125..90f8094 100644
--- a/atr/tasks/checks/rat.py
+++ b/atr/tasks/checks/rat.py
@@ -48,6 +48,7 @@ async def check(args: checks.FunctionArguments) -> str | None:
if not (artifact_abs_path := await recorder.abs_path()):
return None
if await recorder.primary_path_is_binary():
+ _LOGGER.info(f"Skipping RAT check for binary artifact
{artifact_abs_path} (rel: {args.primary_rel_path})")
return None
_LOGGER.info(f"Checking RAT licenses for {artifact_abs_path} (rel:
{args.primary_rel_path})")
diff --git a/poetry.lock b/poetry.lock
index 7ebf38f..e217940 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1045,6 +1045,17 @@ files = [
{file = "frozenlist-1.7.0.tar.gz", hash =
"sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f"},
]
+[[package]]
+name = "gitignore-parser"
+version = "0.1.12"
+description = "A spec-compliant gitignore parser for Python 3.5+"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+ {file = "gitignore_parser-0.1.12.tar.gz", hash =
"sha256:78b22243adc0f02102c56c5e8c9a1d9121394142ca6143a90daa7f8d7a07a17e"},
+]
+
[[package]]
name = "greenlet"
version = "3.2.3"
@@ -3184,4 +3195,4 @@ propcache = ">=0.2.1"
[metadata]
lock-version = "2.1"
python-versions = "~=3.13"
-content-hash =
"b79174b297f7612b074db005aa6399eace902e49302d56e0168540befd71a8f1"
+content-hash =
"54eafdba2809654d5c799509841e7fb6e3cb67635d88f7770de3217dc5c68c44"
diff --git a/pyproject.toml b/pyproject.toml
index 27d71ed..2153c78 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -25,6 +25,7 @@ dependencies = [
"dnspython>=2.7.0,<3.0.0",
"dunamai>=1.23.0",
"email-validator~=2.2.0",
+ "gitignore-parser (>=0.1.12,<0.2.0)",
"greenlet>=3.1.1,<4.0.0",
"httpx~=0.27",
"hypercorn~=0.17",
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]