You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by ar...@apache.org on 2026/06/04 14:55:13 UTC
(tooling-trusted-releases) 02/03: #1298, #1297 - Update downloads URLs to follow foundation policy (after /atr SVN location is gone)
This is an automated email from the ASF dual-hosted git repository.
alitheg pushed a commit to branch arm
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
commit dcd3dc91696e6450d91e40d5d1a969938b8f771a
Author: Alastair McFarlane <ar...@apache.org>
AuthorDate: Thu Jun 4 11:20:29 2026 +0100
#1298, #1297 - Update downloads URLs to follow foundation policy (after /atr SVN location is gone)
---
atr/api/__init__.py | 4 +-
atr/constants.py | 1 +
atr/construct.py | 4 +-
atr/get/announce.py | 3 +-
atr/get/catalog.py | 52 +++++++++++++++++------
atr/models/sql.py | 3 ++
atr/paths.py | 39 +++++++++++++-----
atr/post/keys.py | 8 ++--
atr/storage/writers/announce.py | 10 ++++-
atr/templates/_catalog_version.html | 6 +--
atr/util.py | 55 ++++++++++++++++---------
migrations/versions/0093_2026.06.04_40cbca79.py | 27 ++++++++++++
tests/unit/test_catalog.py | 6 +--
tests/unit/test_svn_publish_helpers.py | 47 +++++++++++++++++++--
14 files changed, 202 insertions(+), 63 deletions(-)
diff --git a/atr/api/__init__.py b/atr/api/__init__.py
index bdcb5fa5..bda6b984 100644
--- a/atr/api/__init__.py
+++ b/atr/api/__init__.py
@@ -1477,7 +1477,9 @@ async def signature_provenance(
signing_keys.append(
models.api.SignatureProvenanceKey(
committee=committee.key,
- keys_file_url=f"{paths.committee_downloads_url(host, committee)}/KEYS",
+ keys_file_url=util.public_download_url(
+ committee, None, util.DownloadFile.METADATA, filename="KEYS", host=host
+ ),
keys_file_sha3_256=keys_file_sha3_256,
)
)
diff --git a/atr/constants.py b/atr/constants.py
index 50780127..869cf0e9 100644
--- a/atr/constants.py
+++ b/atr/constants.py
@@ -17,6 +17,7 @@
from typing import Final
+CLOSER_LUA_URL: Final[str] = "https://www.apache.org/dyn/closer.lua"
DOWNLOADS_APACHE_URL: Final[str] = "https://downloads.apache.org"
SVN_DIST_PUBLIC_URL: Final[str] = "https://dist.apache.org/repos/dist/atr"
SVN_DIST_ROOT_URL: Final[str] = "https://dist.apache.org/repos/dist"
diff --git a/atr/construct.py b/atr/construct.py
index 1c300e43..52672081 100644
--- a/atr/construct.py
+++ b/atr/construct.py
@@ -193,7 +193,7 @@ async def announce_release_subject_and_body(
project = release.project
project_display_name = project.short_display_name if project else str(options.project_key)
- download_url = paths.committee_downloads_dist_url(committee, host)
+ download_url = util.public_download_url(committee, None, util.DownloadFile.METADATA, host=host)
if options.download_path_suffix is not None:
download_url += f"/{options.download_path_suffix!s}"
download_url += "/"
@@ -333,7 +333,7 @@ async def start_vote_subject_and_body(subject: str, body: str, options: StartVot
keys_file = None
keys_file_path = paths.committee_downloads_dir(committee) / "KEYS"
if await aiofiles.os.path.isfile(keys_file_path):
- keys_file = f"{paths.committee_downloads_url(host, committee)}/KEYS"
+ keys_file = util.public_download_url(committee, None, util.DownloadFile.METADATA, filename="KEYS", host=host)
checklist_content = ""
async with db.session() as data:
diff --git a/atr/get/announce.py b/atr/get/announce.py
index 92c4e0b5..f7be6555 100644
--- a/atr/get/announce.py
+++ b/atr/get/announce.py
@@ -29,7 +29,6 @@ import atr.get.projects as projects
import atr.htm as htm
import atr.models.safe as safe
import atr.models.sql as sql
-import atr.paths as paths
import atr.post as post
import atr.render as render
import atr.shared as shared
@@ -91,7 +90,7 @@ async def selected(
default_subject_template, default_body_template, options
)
- description_download_prefix = paths.committee_downloads_dist_url(committee, config.get().APP_HOST)
+ description_download_prefix = util.public_download_url(committee, None, util.DownloadFile.METADATA)
preview_url = util.as_url(
post.announce.preview,
project_key=release.project.key,
diff --git a/atr/get/catalog.py b/atr/get/catalog.py
index b66f5c72..a470ed8a 100644
--- a/atr/get/catalog.py
+++ b/atr/get/catalog.py
@@ -50,7 +50,7 @@ async def project(_session: web.Public, _catalog: Literal["catalog"], project_ke
cycles_by_key = {cycle.cycle_key: cycle for cycle in project_cycles}
now = datetime.datetime.now(datetime.UTC)
- versions = _catalog_versions(artifacts, cycles_by_key)
+ versions = _catalog_versions(artifacts, cycles_by_key, project_obj.committee)
grouped = _grouped_layout(project_obj.version_method)
cycles = _catalog_cycles(versions, now) if grouped else []
@@ -74,6 +74,11 @@ class _CatalogArtifact:
key_fingerprint: str | None
svn_revision: int | None
downloadable: bool
+ # Public download URLs: the artifact via the mirror network, its signature and
+ # checksum from downloads.apache.org. None when the version is not downloadable.
+ artifact_url: str | None
+ signature_url: str | None
+ checksum_url: str | None
@dataclasses.dataclass
@@ -98,9 +103,41 @@ def _grouped_layout(version_method: sql.VersionMethod) -> bool:
return version_method in _GROUPED_METHODS
+def _catalog_artifact(row: sql.Artifact, committee: sql.Committee | None, downloadable: bool) -> _CatalogArtifact:
+ suffix = safe.RelPath(row.download_path_suffix) if row.download_path_suffix else None
+ artifact_url: str | None = None
+ signature_url: str | None = None
+ checksum_url: str | None = None
+ if downloadable and (committee is not None):
+ artifact_url = util.public_download_url(
+ committee, suffix, util.DownloadFile.ARTIFACT, filename=row.artifact_path
+ )
+ if row.signature_path:
+ signature_url = util.public_download_url(
+ committee, suffix, util.DownloadFile.METADATA, filename=row.signature_path
+ )
+ if row.checksum_path:
+ checksum_url = util.public_download_url(
+ committee, suffix, util.DownloadFile.METADATA, filename=row.checksum_path
+ )
+ return _CatalogArtifact(
+ artifact_path=row.artifact_path,
+ classification=row.classification,
+ signature_path=row.signature_path,
+ checksum_path=row.checksum_path,
+ key_fingerprint=row.key_fingerprint,
+ svn_revision=row.svn_revision,
+ downloadable=downloadable,
+ artifact_url=artifact_url,
+ signature_url=signature_url,
+ checksum_url=checksum_url,
+ )
+
+
def _catalog_versions(
artifacts: Sequence[sql.Artifact],
cycles_by_key: dict[str, sql.ProjectCycle],
+ committee: sql.Committee | None,
) -> list[_CatalogVersion]:
by_version: dict[str, list[sql.Artifact]] = collections.defaultdict(list)
for artifact in artifacts:
@@ -121,18 +158,7 @@ def _catalog_versions(
released=(release.released or release.created) if (release is not None) else None,
svn_revision=max(svn_revisions) if svn_revisions else None,
cycle=cycle,
- artifacts=[
- _CatalogArtifact(
- artifact_path=row.artifact_path,
- classification=row.classification,
- signature_path=row.signature_path,
- checksum_path=row.checksum_path,
- key_fingerprint=row.key_fingerprint,
- svn_revision=row.svn_revision,
- downloadable=downloadable,
- )
- for row in rows
- ],
+ artifacts=[_catalog_artifact(row, committee, downloadable) for row in rows],
)
)
diff --git a/atr/models/sql.py b/atr/models/sql.py
index e4db671e..0124059c 100644
--- a/atr/models/sql.py
+++ b/atr/models/sql.py
@@ -1878,6 +1878,9 @@ class Artifact(sqlmodel.SQLModel, table=True):
classification: str | None = sqlmodel.Field(default=None, **example("source"))
# Per-artifact SVN revision - not all artifacts in a release are added in the same commit
svn_revision: int | None = sqlmodel.Field(default=None, index=True, **example(12345))
+ # The release's path under the committee downloads dir, needed to build the public
+ # download URLs. NULL means the files sit directly under the committee dir.
+ download_path_suffix: str | None = sqlmodel.Field(default=None, **example("example/0.0.1"))
# M-1: Artifact -> Release
# 1-M: Release -> [Artifact]
diff --git a/atr/paths.py b/atr/paths.py
index 90798e7b..7e4ac812 100644
--- a/atr/paths.py
+++ b/atr/paths.py
@@ -16,7 +16,6 @@
# under the License.
import pathlib
-import urllib.parse
import atr.config as config
import atr.constants as constants
@@ -30,6 +29,27 @@ def base_path_for_revision(
return get_unfinished_dir() / project_key / version_key / revision
+def closer_download_url(relpath: safe.RelPath) -> str:
+ # For the artifacts themselves we go via the mirror network rather than
+ # downloads.apache.org. action=download skips the human mirror-picker page and
+ # redirects straight to a chosen mirror.
+ return f"{constants.CLOSER_LUA_URL}/{relpath}?action=download"
+
+
+def committee_dist_relpath(
+ committee: sql.Committee, suffix: safe.RelPath | None = None, filename: str | None = None
+) -> safe.RelPath:
+ # The committee's path relative to the downloads root, ie the shared prefix that
+ # the download hosts and the distribution SVN area both hang a release off.
+ # Optionally extended with the per-release download suffix and a file name.
+ relpath = safe.RelPath.from_path(committee_downloads_dir(committee).path.relative_to(get_downloads_dir().path))
+ if suffix is not None:
+ relpath = relpath.append(suffix.as_path())
+ if filename is not None:
+ relpath = relpath.append(filename)
+ return relpath
+
+
def committee_downloads_dir(committee: sql.Committee) -> safe.StatePath:
downloads_dir = get_downloads_dir()
if committee.is_podling:
@@ -37,16 +57,6 @@ def committee_downloads_dir(committee: sql.Committee) -> safe.StatePath:
return downloads_dir / committee.key
-def committee_downloads_dist_url(committee: sql.Committee, host: str) -> str:
- if config.get().SVN_PUBLISH_URL:
- relpath = committee_downloads_dir(committee).path.relative_to(get_downloads_dir().path)
- public_path = urllib.parse.urlparse(constants.SVN_DIST_PUBLIC_URL).path.rstrip("/")
- if public_path.endswith("/release"):
- return f"{constants.DOWNLOADS_APACHE_URL}/{relpath}"
- return f"{constants.SVN_DIST_PUBLIC_URL.rstrip('/')}/{relpath}"
- return committee_downloads_url(host, committee)
-
-
def committee_downloads_url(host: str, committee: sql.Committee) -> str:
# This is a slight extension of the intended paths concept
# But URLs contain paths, so atr.paths does not have to be limited to filesystem paths
@@ -55,6 +65,13 @@ def committee_downloads_url(host: str, committee: sql.Committee) -> str:
return f"https://{host}/downloads/{committee.key}"
+def downloads_url(relpath: safe.RelPath) -> str:
+ # downloads.apache.org itself, not a mirror. Signatures, checksums and KEYS
+ # must come from here rather than a mirror, otherwise a bad mirror could serve
+ # a matching artifact and signature.
+ return f"{constants.DOWNLOADS_APACHE_URL}/{relpath}"
+
+
def get_archives_dir() -> safe.StatePath:
return safe.StatePath(pathlib.Path(config.get().ARCHIVES_STORAGE_DIR))
diff --git a/atr/post/keys.py b/atr/post/keys.py
index e18647a7..58f2b30d 100644
--- a/atr/post/keys.py
+++ b/atr/post/keys.py
@@ -27,7 +27,6 @@ import quart
import atr.blueprints.post as post
import atr.config as config
-import atr.constants as constants
import atr.db as db
import atr.form as form
import atr.get as get
@@ -36,6 +35,7 @@ import atr.log as log
import atr.models.safe as safe
import atr.models.sql as sql
import atr.models.unsafe as unsafe
+import atr.paths as paths
import atr.sessions as sessions
import atr.shared as shared
import atr.storage as storage
@@ -269,9 +269,9 @@ async def _add_key_text_resolve(session: web.Committer, add_form: shared.keys.Ad
def _construct_keys_url(committee_key: str, *, is_podling: bool) -> str:
- if is_podling:
- return f"{constants.DOWNLOADS_APACHE_URL}/incubator/{committee_key}/KEYS"
- return f"{constants.DOWNLOADS_APACHE_URL}/{committee_key}/KEYS"
+ # KEYS comes from downloads.apache.org, not a mirror.
+ prefix = "incubator/" if is_podling else ""
+ return paths.downloads_url(safe.RelPath(f"{prefix}{committee_key}/KEYS"))
async def _delete_openpgp_key(
diff --git a/atr/storage/writers/announce.py b/atr/storage/writers/announce.py
index a5db7405..045a3372 100644
--- a/atr/storage/writers/announce.py
+++ b/atr/storage/writers/announce.py
@@ -222,7 +222,9 @@ class CommitteeMember(CommitteeParticipant):
)
try:
target = util.svn_publish_target()
- public_url = util.svn_publish_public_url(target, committee, effective_download_path_suffix)
+ public_url = util.public_download_url(
+ committee, effective_download_path_suffix, util.DownloadFile.METADATA
+ )
except ValueError as exc:
log.warning(f"SVN publication target is not configured for {project_key!s} {version_key!s}: {exc}")
else:
@@ -292,7 +294,9 @@ class CommitteeMember(CommitteeParticipant):
# Record the artifacts before promoting, since promotion deletes the
# revisions and cascade-deletes the file state we read classifications from
- await self.__write_artifact_rows(release, finished_path, preview_revision_number, published_revision)
+ await self.__write_artifact_rows(
+ release, finished_path, preview_revision_number, published_revision, effective_download_path_suffix
+ )
await self.__promote_in_database(release, preview_revision_number, release_date)
self.__data.add(
sql.LifecycleEvent(
@@ -464,6 +468,7 @@ class CommitteeMember(CommitteeParticipant):
finished_path: safe.StatePath,
revision_number: safe.RevisionNumber,
svn_revision: int | None,
+ download_path_suffix: safe.RelPath | None,
) -> None:
revision_seq = int(str(revision_number))
# The preview revision created when a vote passes has no checks of its own, so the
@@ -498,5 +503,6 @@ class CommitteeMember(CommitteeParticipant):
checksum_path=checksum_path,
classification=classifications.get(rel),
svn_revision=svn_revision,
+ download_path_suffix=str(download_path_suffix) if download_path_suffix is not None else None,
)
)
diff --git a/atr/templates/_catalog_version.html b/atr/templates/_catalog_version.html
index fcdef084..e7ceb4a3 100644
--- a/atr/templates/_catalog_version.html
+++ b/atr/templates/_catalog_version.html
@@ -21,9 +21,9 @@
</span>
<span class="small text-nowrap">
{% if artifact.downloadable %}
- <a href="{{ as_url(get.download.path, project_key=project.key, version_key=version.version, file_path=artifact.artifact_path) }}">Download</a>
- {% if artifact.signature_path %}<a class="ms-2" href="{{ as_url(get.download.path, project_key=project.key, version_key=version.version, file_path=artifact.signature_path) }}">.asc</a>{% endif %}
- {% if artifact.checksum_path %}<a class="ms-2" href="{{ as_url(get.download.path, project_key=project.key, version_key=version.version, file_path=artifact.checksum_path) }}">checksum</a>{% endif %}
+ <a href="{{ artifact.artifact_url }}">Download</a>
+ {% if artifact.signature_url %}<a class="ms-2" href="{{ artifact.signature_url }}">.asc</a>{% endif %}
+ {% if artifact.checksum_url %}<a class="ms-2" href="{{ artifact.checksum_url }}">checksum</a>{% endif %}
{% else %}
<span class="text-secondary">Metadata only</span>
{% endif %}
diff --git a/atr/util.py b/atr/util.py
index 7ddc566e..f8f079cc 100644
--- a/atr/util.py
+++ b/atr/util.py
@@ -172,6 +172,13 @@ class SvnPublishTarget(enum.Enum):
RELEASE = "release"
+class DownloadFile(enum.Enum):
+ # Ultimately decides where a published release file should be fetched from:
+ # artifacts via the mirror network, metadata from the downloads.a.o.
+ ARTIFACT = "artifact"
+ METADATA = "metadata"
+
+
class EmailRecipients(Protocol):
email_to: str
email_cc: list[str]
@@ -993,6 +1000,34 @@ def plural(count: int, singular: str, plural_form: str | None = None, *, include
return word
+def public_download_url(
+ committee: sql.Committee,
+ suffix: safe.RelPath | None,
+ kind: DownloadFile,
+ filename: str | None = None,
+ host: str | None = None,
+) -> str:
+ # The single place that resolves where a release file is fetched from, by
+ # lifecycle (beta vs released) and role (artifact vs metadata).
+ if not config.get().SVN_PUBLISH_URL:
+ # No SVN publishing configured (dev), so ATR serves the downloads itself.
+ url = paths.committee_downloads_url(host or config.get().APP_HOST, committee)
+ if suffix is not None:
+ url = f"{url}/{suffix}"
+ if filename is not None:
+ url = f"{url}/{filename}"
+ return url
+ relpath = paths.committee_dist_relpath(committee, suffix, filename)
+ match svn_publish_target():
+ case SvnPublishTarget.ATR:
+ # Beta: files are still in ATR's area of the distribution SVN, not yet mirrored.
+ return f"{constants.SVN_DIST_PUBLIC_URL.rstrip('/')}/{relpath}"
+ case SvnPublishTarget.RELEASE:
+ if kind is DownloadFile.ARTIFACT:
+ return paths.closer_download_url(relpath)
+ return paths.downloads_url(relpath)
+
+
async def read_file_for_viewer(full_path: safe.StatePath, max_size: int) -> tuple[str | None, bool, bool, str | None]:
"""Read file content for viewer."""
content: str | None = None
@@ -1084,28 +1119,10 @@ def svn_publish_internal_url(
publish_url = config.get().SVN_PUBLISH_URL
if not publish_url:
raise ValueError("SVN_PUBLISH_URL is not configured")
- relpath = paths.committee_downloads_dir(committee).path.relative_to(paths.get_downloads_dir().path)
- if download_path_suffix is not None:
- relpath = relpath / download_path_suffix.as_path()
+ relpath = paths.committee_dist_relpath(committee, download_path_suffix)
return f"{publish_url.rstrip('/')}/{relpath}"
-def svn_publish_public_url(
- target: SvnPublishTarget,
- committee: sql.Committee,
- download_path_suffix: safe.RelPath | None,
-) -> str:
- relpath = paths.committee_downloads_dir(committee).path.relative_to(paths.get_downloads_dir().path)
- if download_path_suffix is not None:
- relpath = relpath / download_path_suffix.as_path()
- match target:
- case SvnPublishTarget.RELEASE:
- base_url = constants.DOWNLOADS_APACHE_URL
- case SvnPublishTarget.ATR:
- base_url = constants.SVN_DIST_PUBLIC_URL
- return f"{base_url.rstrip('/')}/{relpath}"
-
-
def svn_publish_target() -> SvnPublishTarget:
public_path = urllib.parse.urlparse(constants.SVN_DIST_PUBLIC_URL).path.rstrip("/")
if public_path.endswith("/atr"):
diff --git a/migrations/versions/0093_2026.06.04_40cbca79.py b/migrations/versions/0093_2026.06.04_40cbca79.py
new file mode 100644
index 00000000..be8bc23c
--- /dev/null
+++ b/migrations/versions/0093_2026.06.04_40cbca79.py
@@ -0,0 +1,27 @@
+"""Add download_path_suffix to artifacts
+
+Revision ID: 0093_2026.06.04_40cbca79
+Revises: 0092_2026.06.02_87d3e8f1
+Create Date: 2026-06-04 08:53:16.500003+00:00
+"""
+
+from collections.abc import Sequence
+
+import sqlalchemy as sa
+from alembic import op
+
+# Revision identifiers, used by Alembic
+revision: str = "0093_2026.06.04_40cbca79"
+down_revision: str | None = "0092_2026.06.02_87d3e8f1"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
+
+
+def upgrade() -> None:
+ with op.batch_alter_table("artifact", schema=None) as batch_op:
+ batch_op.add_column(sa.Column("download_path_suffix", sa.String(), nullable=True))
+
+
+def downgrade() -> None:
+ with op.batch_alter_table("artifact", schema=None) as batch_op:
+ batch_op.drop_column("download_path_suffix")
diff --git a/tests/unit/test_catalog.py b/tests/unit/test_catalog.py
index 26ebb2c9..8b90c942 100644
--- a/tests/unit/test_catalog.py
+++ b/tests/unit/test_catalog.py
@@ -108,7 +108,7 @@ def test_status_reflects_release_and_archive_state() -> None:
_artifact(project, "4.1.7", "a-4.1.7.tar.gz", release=archived),
]
- versions = {v.version: v for v in catalog._catalog_versions(artifacts, {})}
+ versions = {v.version: v for v in catalog._catalog_versions(artifacts, {}, project.committee_key)}
assert versions["5.0.2"].status == "released"
assert versions["4.1.7"].status == "archived"
@@ -123,7 +123,7 @@ def test_only_released_versions_are_downloadable() -> None:
_artifact(project, "4.1.7", "a-4.1.7.tar.gz", release=archived, svn_revision=28114),
]
- versions = {v.version: v for v in catalog._catalog_versions(artifacts, {})}
+ versions = {v.version: v for v in catalog._catalog_versions(artifacts, {}, project.committee_key)}
assert versions["5.0.2"].artifacts[0].downloadable is True
assert versions["4.1.7"].artifacts[0].downloadable is False
@@ -143,7 +143,7 @@ def test_versions_are_newest_first() -> None:
_artifact(project, "5.0.2", "a-5.0.2.tar.gz", release=newer),
]
- versions = catalog._catalog_versions(artifacts, {})
+ versions = catalog._catalog_versions(artifacts, {}, project.committee_key)
assert [v.version for v in versions] == ["5.0.2", "5.0.1"]
diff --git a/tests/unit/test_svn_publish_helpers.py b/tests/unit/test_svn_publish_helpers.py
index 76d79af2..85a7d1f2 100644
--- a/tests/unit/test_svn_publish_helpers.py
+++ b/tests/unit/test_svn_publish_helpers.py
@@ -119,12 +119,53 @@ def test_svn_publish_models_round_trip() -> None:
assert sql.TaskType.SVN_PUBLISH.label == "SVN publish"
-def test_svn_publish_public_url() -> None:
+def test_public_download_url_uses_svn_dist_area_for_beta_target(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setattr(config.get(), "SVN_PUBLISH_URL", "https://internal.example.invalid/repos/dist/atr")
+ monkeypatch.setattr(constants, "SVN_DIST_PUBLIC_URL", "https://dist.apache.org/repos/dist/atr")
+ committee = sql.Committee(key="apple", name="Apple", is_podling=False)
+
+ url = util.public_download_url(committee, safe.RelPath("apple-1.0"), util.DownloadFile.METADATA)
+
+ assert url == "https://dist.apache.org/repos/dist/atr/apple/apple-1.0"
+
+
+def test_public_download_url_uses_mirror_for_released_artifact(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setattr(config.get(), "SVN_PUBLISH_URL", "https://internal.example.invalid/repos/dist/release")
+ monkeypatch.setattr(constants, "SVN_DIST_PUBLIC_URL", "https://dist.apache.org/repos/dist/release")
+ committee = sql.Committee(key="apple", name="Apple", is_podling=False)
+
+ url = util.public_download_url(
+ committee, safe.RelPath("apple-1.0"), util.DownloadFile.ARTIFACT, filename="apple-1.0.tar.gz"
+ )
+
+ assert url == f"{constants.CLOSER_LUA_URL}/apple/apple-1.0/apple-1.0.tar.gz?action=download"
+
+
+def test_public_download_url_uses_canonical_host_for_released_metadata(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setattr(config.get(), "SVN_PUBLISH_URL", "https://internal.example.invalid/repos/dist/release")
+ monkeypatch.setattr(constants, "SVN_DIST_PUBLIC_URL", "https://dist.apache.org/repos/dist/release")
committee = sql.Committee(key="apple", name="Apple", is_podling=False)
- target_url = util.svn_publish_public_url(util.SvnPublishTarget.ATR, committee, safe.RelPath("apple-1.0"))
+ url = util.public_download_url(
+ committee, safe.RelPath("apple-1.0"), util.DownloadFile.METADATA, filename="apple-1.0.tar.gz.asc"
+ )
+
+ assert url == f"{constants.DOWNLOADS_APACHE_URL}/apple/apple-1.0/apple-1.0.tar.gz.asc"
+
+
+def test_public_download_url_falls_back_to_self_host_without_svn(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setattr(config.get(), "SVN_PUBLISH_URL", None)
+ committee = sql.Committee(key="apple", name="Apple", is_podling=False)
+
+ url = util.public_download_url(
+ committee,
+ safe.RelPath("apple-1.0"),
+ util.DownloadFile.ARTIFACT,
+ filename="apple-1.0.tar.gz",
+ host="atr.example",
+ )
- assert target_url == f"{constants.SVN_DIST_PUBLIC_URL}/apple/apple-1.0"
+ assert url == "https://atr.example/downloads/apple/apple-1.0/apple-1.0.tar.gz"
def test_svn_publish_target_from_public_url(monkeypatch: pytest.MonkeyPatch) -> None:
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org