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 3e1625a Rename and move some distribution interfaces for clarity
3e1625a is described below
commit 3e1625a67818aea724590cfd30e62cfe540949e0
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri Aug 8 15:36:49 2025 +0100
Rename and move some distribution interfaces for clarity
---
atr/routes/distribution.py | 413 +++++++++++++++++------------------
atr/storage/writers/distributions.py | 39 +++-
2 files changed, 235 insertions(+), 217 deletions(-)
diff --git a/atr/routes/distribution.py b/atr/routes/distribution.py
index 1b67255..e0c9ca2 100644
--- a/atr/routes/distribution.py
+++ b/atr/routes/distribution.py
@@ -204,9 +204,9 @@ async def list_get(session: routes.CommitterSession,
project: str, version: str)
block = htm.Block()
- release = await _release_validated(project, version)
+ release = await _release_validated(project, version, staging=None)
staging = release.phase == sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT
- _nav_phase(block, project, version, staging)
+ _html_nav_phase(block, project, version, staging)
record_a_distribution = htpy.a(
".btn.btn-primary",
@@ -245,14 +245,14 @@ async def list_get(session: routes.CommitterSession,
project: str, version: str)
### Platform package version
block.h3[f"{distribution.platform.value.name} {distribution.package}
{distribution.version}"]
tbody = htpy.tbody[
- _tr("Release name", distribution.release_name),
- _tr("Platform", distribution.platform.value.name),
- _tr("Owner or Namespace", distribution.owner_namespace or "-"),
- _tr("Package", distribution.package),
- _tr("Version", distribution.version),
- _tr("Staging", "Yes" if distribution.staging else "No"),
- _tr("Upload date", str(distribution.upload_date)),
- _tr("API URL", distribution.api_url),
+ _html_tr("Release name", distribution.release_name),
+ _html_tr("Platform", distribution.platform.value.name),
+ _html_tr("Owner or Namespace", distribution.owner_namespace or
"-"),
+ _html_tr("Package", distribution.package),
+ _html_tr("Version", distribution.version),
+ _html_tr("Staging", "Yes" if distribution.staging else "No"),
+ _html_tr("Upload date", str(distribution.upload_date)),
+ _html_tr("API URL", distribution.api_url),
]
block.table(".table.table-striped.table-bordered")[tbody]
form_action = util.as_url(delete, project=project, version=version)
@@ -271,7 +271,7 @@ async def list_get(session: routes.CommitterSession,
project: str, version: str)
async def record(session: routes.CommitterSession, project: str, version: str)
-> str:
form = await DistributeForm.create_form(data={"package": project,
"version": version})
fpv = FormProjectVersion(form=form, project=project, version=version)
- return await _distribute_page(fpv)
+ return await _record_form_page(fpv)
@routes.committer("/distribution/record/<project>/<version>", methods=["POST"])
@@ -279,7 +279,7 @@ async def record_post(session: routes.CommitterSession,
project: str, version: s
form = await DistributeForm.create_form(data=await quart.request.form)
fpv = FormProjectVersion(form=form, project=project, version=version)
if await form.validate():
- return await _distribute_post_validated(fpv)
+ return await _record_form_process_page(fpv)
match len(form.errors):
case 0:
# Should not happen
@@ -288,14 +288,14 @@ async def record_post(session: routes.CommitterSession,
project: str, version: s
await quart.flash("There was 1 submission error", category="error")
case _ as n:
await quart.flash(f"There were {n} submission errors",
category="error")
- return await _distribute_page(fpv)
+ return await _record_form_page(fpv)
@routes.committer("/distribution/stage/<project>/<version>", methods=["GET"])
async def stage(session: routes.CommitterSession, project: str, version: str)
-> str:
form = await DistributeForm.create_form(data={"package": project,
"version": version})
fpv = FormProjectVersion(form=form, project=project, version=version)
- return await _distribute_page(fpv, staging=True)
+ return await _record_form_page(fpv, staging=True)
@routes.committer("/distribution/stage/<project>/<version>", methods=["POST"])
@@ -303,7 +303,7 @@ async def stage_post(session: routes.CommitterSession,
project: str, version: st
form = await DistributeForm.create_form(data=await quart.request.form)
fpv = FormProjectVersion(form=form, project=project, version=version)
if await form.validate():
- return await _distribute_post_validated(fpv, staging=True)
+ return await _record_form_process_page(fpv, staging=True)
match len(form.errors):
case 0:
await quart.flash("Ambiguous submission errors",
category="warning")
@@ -311,19 +311,141 @@ async def stage_post(session: routes.CommitterSession,
project: str, version: st
await quart.flash("There was 1 submission error", category="error")
case _ as n:
await quart.flash(f"There were {n} submission errors",
category="error")
- return await _distribute_page(fpv, staging=True)
+ return await _record_form_page(fpv, staging=True)
-# This function is used in both GET and POST routes
-async def _distribute_page(
+def _distribution_upload_date( # noqa: C901
+ platform: sql.DistributionPlatform,
+ data: basic.JSON,
+ version: str,
+) -> datetime.datetime | None:
+ match platform:
+ case sql.DistributionPlatform.ARTIFACTHUB:
+ if not (versions :=
ArtifactHubResponse.model_validate(data).available_versions):
+ return None
+ return datetime.datetime.fromtimestamp(versions[0].ts,
tz=datetime.UTC)
+ case sql.DistributionPlatform.DOCKER:
+ if not (pushed_at :=
DockerResponse.model_validate(data).tag_last_pushed):
+ return None
+ return datetime.datetime.fromisoformat(pushed_at.rstrip("Z"))
+ case sql.DistributionPlatform.GITHUB:
+ if not (published_at :=
GitHubResponse.model_validate(data).published_at):
+ return None
+ return datetime.datetime.fromisoformat(published_at.rstrip("Z"))
+ case sql.DistributionPlatform.MAVEN:
+ if not (docs :=
MavenResponse.model_validate(data).response.get("docs")):
+ return None
+ if not (timestamp := docs[0].timestamp):
+ return None
+ return datetime.datetime.fromtimestamp(timestamp / 1000,
tz=datetime.UTC)
+ case sql.DistributionPlatform.NPM |
sql.DistributionPlatform.NPM_SCOPED:
+ if not (times := NpmResponse.model_validate(data).time):
+ return None
+ # Versions can be in the form "1.2.3" or "v1.2.3", so we check for
both
+ if not (upload_time := times.get(version) or
times.get(f"v{version}")):
+ return None
+ return datetime.datetime.fromisoformat(upload_time.rstrip("Z"))
+ case sql.DistributionPlatform.PYPI:
+ if not (urls := PyPIResponse.model_validate(data).urls):
+ return None
+ if not (upload_time := urls[0].upload_time_iso_8601):
+ return None
+ return datetime.datetime.fromisoformat(upload_time.rstrip("Z"))
+ raise NotImplementedError(f"Platform {platform.name} is not yet supported")
+
+
+async def _json_from_distribution_platform(
+ api_url: str, platform: sql.DistributionPlatform, version: str
+) -> outcome.Outcome[basic.JSON]:
+ try:
+ async with aiohttp.ClientSession() as session:
+ async with session.get(api_url) as response:
+ response.raise_for_status()
+ response_json = await response.json()
+ result = basic.as_json(response_json)
+ except aiohttp.ClientError as e:
+ return outcome.Error(e)
+ match platform:
+ case sql.DistributionPlatform.NPM |
sql.DistributionPlatform.NPM_SCOPED:
+ if version not in NpmResponse.model_validate(result).time:
+ e = RuntimeError(f"Version '{version}' not found")
+ return outcome.Error(e)
+ return outcome.Result(result)
+
+
+# TODO: Move this to an appropriate module
+def _html_nav(container: htm.Block, back_url: str, back_anchor: str, phase:
Phase) -> None:
+ classes = ".d-flex.justify-content-between.align-items-center"
+ block = htm.Block(htpy.p(classes))
+ block.a(".atr-back-link", href=back_url)[f"← Back to {back_anchor}"]
+ span = htm.Block(htpy.span)
+
+ def _phase(actual: Phase, expected: Phase) -> None:
+ nonlocal span
+ match expected:
+ case "COMPOSE":
+ symbol = "①"
+ case "VOTE":
+ symbol = "②"
+ case "FINISH":
+ symbol = "③"
+ if actual == expected:
+ span.strong(f".atr-phase-{actual}.atr-phase-symbol")[symbol]
+ span.span(f".atr-phase-{actual}.atr-phase-label")[actual]
+ else:
+ span.span(".atr-phase-symbol-other")[symbol]
+
+ _phase(phase, "COMPOSE")
+ span.span(".atr-phase-arrow")["→"]
+ _phase(phase, "VOTE")
+ span.span(".atr-phase-arrow")["→"]
+ _phase(phase, "FINISH")
+
+ block.append(span.collect(separator=" "))
+ container.append(block.collect())
+
+
+def _html_nav_phase(block: htm.Block, project: str, version: str, staging:
bool) -> None:
+ label: Phase
+ route, label = (compose.selected, "COMPOSE")
+ if not staging:
+ route, label = (finish.selected, "FINISH")
+ _html_nav(
+ block,
+ util.as_url(
+ route,
+ project_name=project,
+ version_name=version,
+ ),
+ back_anchor=f"{label.title()} {project} {version}",
+ phase=label,
+ )
+
+
+def _html_submitted_values_table(block: htm.Block, dd: DistributeData) -> None:
+ tbody = htpy.tbody[
+ _html_tr("Platform", dd.platform.name),
+ _html_tr("Owner or Namespace", dd.owner_namespace or "-"),
+ _html_tr("Package", dd.package),
+ _html_tr("Version", dd.version),
+ ]
+ block.table(".table.table-striped.table-bordered")[tbody]
+
+
+def _html_tr(label: str, value: str) -> htpy.Element:
+ return htpy.tr[htpy.th[label], htpy.td[value]]
+
+
+# This function is used for COMPOSE (stage) and FINISH (record)
+# It's also used whenever there is an error
+async def _record_form_page(
fpv: FormProjectVersion, *, extra_content: htpy.Element | None = None,
staging: bool = False
) -> str:
- phase = sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT if staging else
sql.ReleasePhase.RELEASE_PREVIEW
- await _release_validated(fpv.project, fpv.version, phase={phase})
+ await _release_validated(fpv.project, fpv.version, staging=staging)
# Render the explanation and form
block = htm.Block()
- _nav_phase(block, fpv.project, fpv.version, staging)
+ _html_nav_phase(block, fpv.project, fpv.version, staging)
# Record a manual distribution
title_and_heading = f"Record a {'staging' if staging else 'manual'}
distribution"
@@ -346,89 +468,18 @@ async def _distribute_page(
return await template.blank(title_and_heading, content=block.collect())
-async def _distribute_post_api(
- api_url: str, platform: sql.DistributionPlatform, version: str
-) -> outcome.Outcome[basic.JSON]:
- try:
- async with aiohttp.ClientSession() as session:
- async with session.get(api_url) as response:
- response.raise_for_status()
- response_json = await response.json()
- result = basic.as_json(response_json)
- except aiohttp.ClientError as e:
- return outcome.Error(e)
- match platform:
- case sql.DistributionPlatform.NPM |
sql.DistributionPlatform.NPM_SCOPED:
- if version not in NpmResponse.model_validate(result).time:
- e = RuntimeError(f"Version '{version}' not found")
- return outcome.Error(e)
- return outcome.Result(result)
-
-
-async def _resolve_release_committee_and_template(
- fpv: FormProjectVersion,
- dd: DistributeData,
- staging: bool,
-) -> tuple[sql.Release, sql.Committee, str] | htpy.Element:
- if staging:
- release, committee = await _release_committee_validated(
- fpv.project, fpv.version,
phase={sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT}
- )
- supported = {sql.DistributionPlatform.ARTIFACTHUB,
sql.DistributionPlatform.PYPI}
- if dd.platform not in supported:
- div = htm.Block(htpy.div(".alert.alert-danger"))
- div.p["Staging is currently supported only for ArtifactHub and
PyPI."]
- return div.collect()
- template_url = dd.platform.value.template_staging_url
- if template_url is None:
- div = htm.Block(htpy.div(".alert.alert-danger"))
- div.p["This platform does not provide a staging API endpoint."]
- return div.collect()
- return release, committee, template_url
- release, committee = await _release_committee_validated(fpv.project,
fpv.version)
- return release, committee, dd.platform.value.template_url
-
-
-async def _maybe_upgrade_existing_final(
- release: sql.Release,
- dd: DistributeData,
- upload_date: datetime.datetime,
- api_url: str,
- added: bool,
- staging: bool,
- distribution: sql.Distribution,
-) -> sql.Distribution:
- # TODO: Move this to the storage interface
- if (not added) and (not staging):
- async with db.session() as data:
- existing = await data.distribution(
- release_name=release.name,
- platform=dd.platform,
- owner_namespace=dd.owner_namespace or "",
- package=dd.package,
- version=dd.version,
- ).demand(RuntimeError("Distribution not found"))
- if existing.staging:
- existing.staging = False
- existing.upload_date = upload_date
- existing.api_url = api_url
- await data.commit()
- return existing
- return distribution
-
-
-async def _distribute_post_validated(fpv: FormProjectVersion, /, staging: bool
= False) -> str:
+async def _record_form_process_page(fpv: FormProjectVersion, /, staging: bool
= False) -> str:
dd = DistributeData.model_validate(fpv.form.data)
- resolved = await _resolve_release_committee_and_template(fpv, dd, staging)
+ resolved = await _release_validated_and_committee_and_template(fpv, dd,
staging)
if isinstance(resolved, htpy.Element):
- return await _distribute_page(fpv, extra_content=resolved,
staging=staging)
+ return await _record_form_page(fpv, extra_content=resolved,
staging=staging)
release, committee, template_url = resolved
api_url = template_url.format(
owner_namespace=dd.owner_namespace,
package=dd.package,
version=dd.version,
)
- api_oc = await _distribute_post_api(api_url, dd.platform, dd.version)
+ api_oc = await _json_from_distribution_platform(api_url, dd.platform,
dd.version)
block = htm.Block()
@@ -449,15 +500,15 @@ async def _distribute_post_validated(fpv:
FormProjectVersion, /, staging: bool =
pass
case outcome.Error():
alert = _alert("package and version", "check the package name and
version")
- return await _distribute_page(fpv, extra_content=alert,
staging=staging)
+ return await _record_form_page(fpv, extra_content=alert,
staging=staging)
# We leak result, usefully, from this scope
# This must come after the api_oc match, as it uses the result
- upload_date = _platform_upload_date(dd.platform, result, dd.version)
+ upload_date = _distribution_upload_date(dd.platform, result, dd.version)
if upload_date is None:
# TODO: Add a link to an issue tracker
alert = _alert("upload date", "report this bug to ASF Tooling")
- return await _distribute_page(fpv, extra_content=alert,
staging=staging)
+ return await _record_form_page(fpv, extra_content=alert,
staging=staging)
async with
storage.write_as_committee_member(committee_name=committee.name) as w:
distribution, added = await w.distributions.add_distribution(
@@ -470,9 +521,6 @@ async def _distribute_post_validated(fpv:
FormProjectVersion, /, staging: bool =
upload_date=upload_date,
api_url=api_url,
)
- distribution = await _maybe_upgrade_existing_final(
- release, dd, upload_date, api_url, added, staging, distribution
- )
### Record
block.h2["Record"]
@@ -482,14 +530,14 @@ async def _distribute_post_validated(fpv:
FormProjectVersion, /, staging: bool =
block.p["The distribution was already recorded."]
block.table(".table.table-striped.table-bordered")[
htpy.tbody[
- _tr("Release name", distribution.release_name),
- _tr("Platform", distribution.platform.name),
- _tr("Owner or Namespace", distribution.owner_namespace or "-"),
- _tr("Package", distribution.package),
- _tr("Version", distribution.version),
- _tr("Staging", "Yes" if distribution.staging else "No"),
- _tr("Upload date", str(distribution.upload_date)),
- _tr("API URL", distribution.api_url),
+ _html_tr("Release name", distribution.release_name),
+ _html_tr("Platform", distribution.platform.name),
+ _html_tr("Owner or Namespace", distribution.owner_namespace or
"-"),
+ _html_tr("Package", distribution.package),
+ _html_tr("Version", distribution.version),
+ _html_tr("Staging", "Yes" if distribution.staging else "No"),
+ _html_tr("Upload date", str(distribution.upload_date)),
+ _html_tr("API URL", distribution.api_url),
]
]
block.p[htpy.a(href=util.as_url(list_get, project=fpv.project,
version=fpv.version))["Back to distribution list"],]
@@ -500,7 +548,7 @@ async def _distribute_post_validated(fpv:
FormProjectVersion, /, staging: bool =
### Submitted values
block.h3["Submitted values"]
- _distribute_post_table(block, dd)
+ _html_submitted_values_table(block, dd)
### As JSON
block.h3["As JSON"]
@@ -520,111 +568,13 @@ async def _distribute_post_validated(fpv:
FormProjectVersion, /, staging: bool =
return await template.blank("Distribution submitted",
content=block.collect())
-def _distribute_post_table(block: htm.Block, dd: DistributeData) -> None:
- tbody = htpy.tbody[
- _tr("Platform", dd.platform.name),
- _tr("Owner or Namespace", dd.owner_namespace or "-"),
- _tr("Package", dd.package),
- _tr("Version", dd.version),
- ]
- block.table(".table.table-striped.table-bordered")[tbody]
-
-
-# TODO: Move this to an appropriate module
-def _nav(container: htm.Block, back_url: str, back_anchor: str, phase: Phase)
-> None:
- classes = ".d-flex.justify-content-between.align-items-center"
- block = htm.Block(htpy.p(classes))
- block.a(".atr-back-link", href=back_url)[f"← Back to {back_anchor}"]
- span = htm.Block(htpy.span)
-
- def _phase(actual: Phase, expected: Phase) -> None:
- nonlocal span
- match expected:
- case "COMPOSE":
- symbol = "①"
- case "VOTE":
- symbol = "②"
- case "FINISH":
- symbol = "③"
- if actual == expected:
- span.strong(f".atr-phase-{actual}.atr-phase-symbol")[symbol]
- span.span(f".atr-phase-{actual}.atr-phase-label")[actual]
- else:
- span.span(".atr-phase-symbol-other")[symbol]
-
- _phase(phase, "COMPOSE")
- span.span(".atr-phase-arrow")["→"]
- _phase(phase, "VOTE")
- span.span(".atr-phase-arrow")["→"]
- _phase(phase, "FINISH")
-
- block.append(span.collect(separator=" "))
- container.append(block.collect())
-
-
-def _nav_phase(block: htm.Block, project: str, version: str, staging: bool) ->
None:
- label: Phase
- route, label = (compose.selected, "COMPOSE") if staging else
(finish.selected, "FINISH")
- back_url = util.as_url(
- route,
- project_name=project,
- version_name=version,
- )
- _nav(
- block,
- back_url,
- back_anchor=f"{label.title()} {project} {version}",
- phase=label,
- )
-
-
-def _platform_upload_date( # noqa: C901
- platform: sql.DistributionPlatform,
- data: basic.JSON,
- version: str,
-) -> datetime.datetime | None:
- match platform:
- case sql.DistributionPlatform.ARTIFACTHUB:
- if not (versions :=
ArtifactHubResponse.model_validate(data).available_versions):
- return None
- return datetime.datetime.fromtimestamp(versions[0].ts,
tz=datetime.UTC)
- case sql.DistributionPlatform.DOCKER:
- if not (pushed_at :=
DockerResponse.model_validate(data).tag_last_pushed):
- return None
- return datetime.datetime.fromisoformat(pushed_at.rstrip("Z"))
- case sql.DistributionPlatform.GITHUB:
- if not (published_at :=
GitHubResponse.model_validate(data).published_at):
- return None
- return datetime.datetime.fromisoformat(published_at.rstrip("Z"))
- case sql.DistributionPlatform.MAVEN:
- if not (docs :=
MavenResponse.model_validate(data).response.get("docs")):
- return None
- if not (timestamp := docs[0].timestamp):
- return None
- return datetime.datetime.fromtimestamp(timestamp / 1000,
tz=datetime.UTC)
- case sql.DistributionPlatform.NPM |
sql.DistributionPlatform.NPM_SCOPED:
- if not (times := NpmResponse.model_validate(data).time):
- return None
- # Versions can be in the form "1.2.3" or "v1.2.3", so we check for
both
- if not (upload_time := times.get(version) or
times.get(f"v{version}")):
- return None
- return datetime.datetime.fromisoformat(upload_time.rstrip("Z"))
- case sql.DistributionPlatform.PYPI:
- if not (urls := PyPIResponse.model_validate(data).urls):
- return None
- if not (upload_time := urls[0].upload_time_iso_8601):
- return None
- return datetime.datetime.fromisoformat(upload_time.rstrip("Z"))
- raise NotImplementedError(f"Platform {platform.name} is not yet supported")
-
-
-async def _release_committee_validated(
+async def _release_validated_and_committee(
project: str,
version: str,
*,
- phase: set[sql.ReleasePhase] = {sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT,
sql.ReleasePhase.RELEASE_PREVIEW},
+ staging: bool | None = None,
) -> tuple[sql.Release, sql.Committee]:
- release = await _release_validated(project, version, committee=True,
phase=phase)
+ release = await _release_validated(project, version, committee=True,
staging=staging)
committee = release.committee
if committee is None:
raise RuntimeError(f"Release {project} {version} has no committee")
@@ -635,8 +585,15 @@ async def _release_validated(
project: str,
version: str,
committee: bool = False,
- phase: set[sql.ReleasePhase] = {sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT,
sql.ReleasePhase.RELEASE_PREVIEW},
+ staging: bool | None = None,
) -> sql.Release:
+ match staging:
+ case True:
+ phase = {sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT}
+ case False:
+ phase = {sql.ReleasePhase.RELEASE_PREVIEW}
+ case None:
+ phase = {sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT,
sql.ReleasePhase.RELEASE_PREVIEW}
async with db.session() as data:
release = await data.release(
project_name=project,
@@ -644,11 +601,35 @@ async def _release_validated(
_committee=committee,
).demand(RuntimeError(f"Release {project} {version} not found"))
if release.phase not in phase:
- raise RuntimeError(f"Release {project} {version} is not a {', or
'.join(phase)}")
+ raise RuntimeError(f"Release {project} {version} is not in
{phase}")
# if release.project.status != sql.ProjectStatus.ACTIVE:
# raise RuntimeError(f"Project {project} is not active")
return release
-def _tr(label: str, value: str) -> htpy.Element:
- return htpy.tr[htpy.th[label], htpy.td[value]]
+async def _release_validated_and_committee_and_template(
+ fpv: FormProjectVersion,
+ dd: DistributeData,
+ staging: bool | None = None,
+) -> tuple[sql.Release, sql.Committee, str] | htpy.Element:
+ release, committee = await _release_validated_and_committee(
+ fpv.project,
+ fpv.version,
+ staging=staging,
+ )
+ if staging is False:
+ return release, committee, dd.platform.value.template_url
+
+ supported = {sql.DistributionPlatform.ARTIFACTHUB,
sql.DistributionPlatform.PYPI}
+ if dd.platform not in supported:
+ div = htm.Block(htpy.div(".alert.alert-danger"))
+ div.p["Staging is currently supported only for ArtifactHub and PyPI."]
+ return div.collect()
+
+ template_url = dd.platform.value.template_staging_url
+ if template_url is None:
+ div = htm.Block(htpy.div(".alert.alert-danger"))
+ div.p["This platform does not provide a staging API endpoint."]
+ return div.collect()
+
+ return release, committee, template_url
diff --git a/atr/storage/writers/distributions.py
b/atr/storage/writers/distributions.py
index 03b3319..3421222 100644
--- a/atr/storage/writers/distributions.py
+++ b/atr/storage/writers/distributions.py
@@ -121,11 +121,48 @@ class CommitteeMember(CommitteeParticipant):
# e.orig.sqlite_errorname == "SQLITE_CONSTRAINT_PRIMARYKEY"
match e.orig:
case
sqlite3.IntegrityError(sqlite_errorcode=sqlite3.SQLITE_CONSTRAINT_PRIMARYKEY):
- # log.debug(f"Distribution already exists: {distribution}")
+ if not staging:
+ upgraded = await self.__upgrade_staging_to_final(
+ release_name,
+ platform,
+ owner_namespace,
+ package,
+ version,
+ upload_date,
+ api_url,
+ )
+ if upgraded is not None:
+ return upgraded, False
return distribution, False
raise e
return distribution, True
+ async def __upgrade_staging_to_final(
+ self,
+ release_name: str,
+ platform: sql.DistributionPlatform,
+ owner_namespace: str | None,
+ package: str,
+ version: str,
+ upload_date: datetime.datetime | None,
+ api_url: str,
+ ) -> sql.Distribution | None:
+ tag = f"{release_name} {platform} {owner_namespace or ''} {package}
{version}"
+ existing = await self.__data.distribution(
+ release_name=release_name,
+ platform=platform,
+ owner_namespace=(owner_namespace or ""),
+ package=package,
+ version=version,
+ ).demand(RuntimeError(f"Distribution {tag} not found"))
+ if existing.staging:
+ existing.staging = False
+ existing.upload_date = upload_date
+ existing.api_url = api_url
+ await self.__data.commit()
+ return existing
+ return None
+
async def delete_distribution(
self,
release_name: str,
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]