You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by sb...@apache.org on 2026/08/13 19:06:12 UTC
(tooling-trusted-releases) branch sbp updated: Ignore releases being finalised during validation
This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch sbp
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/sbp by this push:
new 6d1a0e91 Ignore releases being finalised during validation
6d1a0e91 is described below
commit 6d1a0e91fef9af5fad169cda0c97448825c5a335
Author: Sean B. Palmer <se...@miscoranda.com>
AuthorDate: Thu Aug 13 20:04:54 2026 +0100
Ignore releases being finalised during validation
---
atr/validate.py | 23 +++++++---
tests/unit/test_validate_on_disk.py | 87 +++++++++++++++++++++++++++++++++++++
2 files changed, 105 insertions(+), 5 deletions(-)
diff --git a/atr/validate.py b/atr/validate.py
index 813ad067..740b5d3a 100644
--- a/atr/validate.py
+++ b/atr/validate.py
@@ -233,6 +233,15 @@ async def everything(data: db.Session) -> AsyncAnnotatedDivergences:
committees_sorted = await data.committee(_child_committees=True).order_by(sql.Committee.key).all()
projects_sorted = await data.project(_distribution_channels=True).order_by(sql.Project.key).all()
releases_sorted = await data.release().order_by(sql.Release.key).all()
+ finalising_tasks = await data.task(
+ status_in=[sql.TaskStatus.QUEUED, sql.TaskStatus.ACTIVE],
+ task_type=sql.TaskType.RELEASE_FINALISE,
+ ).all()
+ finalising_releases = {
+ (task.project_key, task.version_key)
+ for task in finalising_tasks
+ if (task.project_key is not None) and (task.version_key is not None)
+ }
for c in await asyncio.to_thread(committees, committees_sorted):
yield c
@@ -240,7 +249,7 @@ async def everything(data: db.Session) -> AsyncAnnotatedDivergences:
for p in await asyncio.to_thread(projects, projects_sorted):
yield p
- for r in await asyncio.to_thread(releases, releases_sorted):
+ for r in await asyncio.to_thread(releases, releases_sorted, finalising_releases):
yield r
@@ -399,11 +408,12 @@ def projects(ps: Iterable[sql.Project]) -> AnnotatedDivergences:
yield from project(p)
-def release(r: sql.Release) -> AnnotatedDivergences:
+def release(r: sql.Release, finalising: bool = False) -> AnnotatedDivergences:
"""Check that a release is valid."""
yield from release_created(r)
yield from release_key(r)
- yield from release_on_disk(r)
+ if (r.phase != sql.ReleasePhase.RELEASE) or (not finalising):
+ yield from release_on_disk(r)
yield from release_package_managers(r)
yield from release_released(r)
yield from release_sboms(r)
@@ -517,7 +527,10 @@ def release_vote_logic(r: sql.Release) -> Divergences:
yield from divergences_predicate(okay, expected, actual)
-def releases(rs: Iterable[sql.Release]) -> AnnotatedDivergences:
+def releases(
+ rs: Iterable[sql.Release], finalising_releases: set[tuple[str, str]] | None = None
+) -> AnnotatedDivergences:
"""Check that the releases are valid."""
+ finalising_releases = finalising_releases or set()
for r in rs:
- yield from release(r)
+ yield from release(r, (r.project_key, r.version) in finalising_releases)
diff --git a/tests/unit/test_validate_on_disk.py b/tests/unit/test_validate_on_disk.py
index 079dab12..7647feef 100644
--- a/tests/unit/test_validate_on_disk.py
+++ b/tests/unit/test_validate_on_disk.py
@@ -15,16 +15,37 @@
# specific language governing permissions and limitations
# under the License.
+import datetime
import pathlib
import unittest.mock as mock
+from collections.abc import AsyncIterator
import pytest
+import sqlalchemy.ext.asyncio
+import sqlalchemy.pool
+import sqlmodel
+import atr.db as db
+import atr.models.results as results
import atr.models.safe as safe
import atr.models.sql as sql
import atr.validate as validate
+@pytest.fixture
+async def sqlite_sessionmaker() -> AsyncIterator[sqlalchemy.ext.asyncio.async_sessionmaker[db.Session]]:
+ engine = sqlalchemy.ext.asyncio.create_async_engine(
+ "sqlite+aiosqlite:///:memory:",
+ connect_args={"check_same_thread": False},
+ poolclass=sqlalchemy.pool.StaticPool,
+ )
+ async with engine.begin() as conn:
+ await conn.run_sync(sqlmodel.SQLModel.metadata.create_all)
+ sessionmaker = sqlalchemy.ext.asyncio.async_sessionmaker(bind=engine, class_=db.Session, expire_on_commit=False)
+ yield sessionmaker
+ await engine.dispose()
+
+
def release(phase: sql.ReleasePhase) -> mock.MagicMock:
result = mock.MagicMock()
result.phase = phase
@@ -63,3 +84,69 @@ def test_release_on_disk_passes_released_without_directories(
divergences = list(validate.release_on_disk(release(sql.ReleasePhase.RELEASE)))
assert divergences == []
+
+
+@pytest.mark.parametrize(
+ ("status", "task_type", "task_version", "expected_divergences"),
+ [
+ (sql.TaskStatus.QUEUED, sql.TaskType.RELEASE_FINALISE, "1.0", 0),
+ (sql.TaskStatus.ACTIVE, sql.TaskType.RELEASE_FINALISE, "1.0", 0),
+ (sql.TaskStatus.ACTIVE, sql.TaskType.RELEASE_FINALISE, "2.0", 1),
+ (sql.TaskStatus.ACTIVE, sql.TaskType.SVN_PUBLISH, "1.0", 1),
+ (sql.TaskStatus.COMPLETED, sql.TaskType.RELEASE_FINALISE, "1.0", 1),
+ (sql.TaskStatus.FAILED, sql.TaskType.RELEASE_FINALISE, "1.0", 1),
+ (sql.TaskStatus.BROKEN, sql.TaskType.RELEASE_FINALISE, "1.0", 1),
+ ],
+)
+async def test_release_on_disk_accounts_for_finalisation_task_status(
+ sqlite_sessionmaker,
+ tmp_path: pathlib.Path,
+ monkeypatch: pytest.MonkeyPatch,
+ status: sql.TaskStatus,
+ task_type: sql.TaskType,
+ task_version: str,
+ expected_divergences: int,
+) -> None:
+ unfinished = tmp_path / "unfinished"
+ (unfinished / "proj" / "1.0").mkdir(parents=True)
+ monkeypatch.setattr(validate.paths, "get_unfinished_dir", lambda: safe.StatePath(unfinished))
+ created = datetime.datetime(2026, 1, 1, tzinfo=datetime.UTC)
+ async with sqlite_sessionmaker() as data:
+ data.add(sql.Committee(key="proj", name="Proj"))
+ data.add(sql.Project(key="proj", name="Apache Proj", committee_key="proj", created=created))
+ await data.commit()
+ data.add(
+ sql.Release(
+ phase=sql.ReleasePhase.RELEASE,
+ created=created,
+ project_key="proj",
+ version="1.0",
+ )
+ )
+ data.add(
+ sql.Task(
+ status=status,
+ task_type=task_type,
+ task_args={},
+ asf_uid="user",
+ started=created if status == sql.TaskStatus.ACTIVE else None,
+ pid=1 if status == sql.TaskStatus.ACTIVE else None,
+ completed=created
+ if status in (sql.TaskStatus.COMPLETED, sql.TaskStatus.FAILED, sql.TaskStatus.BROKEN)
+ else None,
+ result=results.ReleaseFinalise(kind="release_finalise", audit_events=1, message="Finalised")
+ if status == sql.TaskStatus.COMPLETED
+ else None,
+ error="Failed" if status in (sql.TaskStatus.FAILED, sql.TaskStatus.BROKEN) else None,
+ project_key="proj",
+ version_key=task_version,
+ revision_number="00001",
+ )
+ )
+ await data.commit()
+
+ divergences = [
+ divergence async for divergence in validate.everything(data) if divergence.validator == "release_on_disk"
+ ]
+
+ assert len(divergences) == expected_divergences
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org