asf-tooling commented on issue #751:
URL: 
https://github.com/apache/tooling-trusted-releases/issues/751#issuecomment-4404154914

   <!-- gofannon-issue-triage-bot v2 -->
   
   **Automated triage** — analyzed at `main@837830e8`
   
   **Type:** `discussion`  •  **Classification:** `no_action`  •  
**Confidence:** `high`
   **Application domain(s):** `release_lifecycle`, `distribution_publishing`
   
   ### Summary
   This is an active investigation/discussion about whether ATR should support 
remote promotion of artifacts on third-party platforms instead of re-uploading. 
@dave2wave set up a platform capability matrix and identified 
`atr/models/sql.py` and `atr/models/distribution.py` as the key files to 
extend. @andrewmusselman provided a comprehensive investigation draft and 
follow-up RFC with corrections and schema proposals, but no final decision or 
approved implementation exists yet. The team is still deliberating 
platform-by-platform (Maven promotion via RAO is historical; PyPI has no 
promotion; Docker/npm use mutable tags; Hex isn't modeled at all).
   
   ### Where this lives in the code today
   
   #### `atr/storage/writers/distributions.py` — 
`CommitteeMember.__upgrade_staging_to_final` (lines 272-301)
   _currently does this_
   Already implements the concept of upgrading a staging distribution record to 
production in the database, which is the local side of what remote promotion 
would need.
   
   ```python
       async def __upgrade_staging_to_final(
           self,
           release_key: models.safe.ReleaseKey,
           platform: models.sql.DistributionPlatform,
           owner_namespace: str | None,
           package: str,
           version: str,
           pending: bool,
           upload_date: datetime.datetime | None,
           api_url: str | None,
           web_url: str | None,
       ) -> models.sql.Distribution | None:
           tag = f"{release_key} {platform} {owner_namespace or ''} {package} 
{version}"
           existing = await self.__data.distribution(
               release_key=str(release_key),
               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.pending = pending
               existing.upload_date = upload_date
               existing.api_url = api_url
               existing.web_url = web_url
               existing.created_by = self.__asf_uid
               await self.__data.commit()
               return existing
           return None
   ```
   
   #### `atr/shared/distribution.py` — `_template_url` (lines 439-458)
   _needs modification_
   Currently limits staging to ArtifactHub, PyPI, and Maven Central. Would need 
extension if Docker Hub, npm, or other platforms gain staging/promotion support.
   
   ```python
   def _template_url(
       dd: distribution.Data,
       staging: bool | None = None,
   ) -> str:
       if staging is False:
           return dd.platform.value.template_url
   
       supported = {
           sql.DistributionPlatform.ARTIFACT_HUB,
           sql.DistributionPlatform.PYPI,
           sql.DistributionPlatform.MAVEN,
       }
       if dd.platform not in supported:
           raise RuntimeError("Staging is currently supported only for 
ArtifactHub, PyPI and Maven Central.")
   
       template_url = dd.platform.value.template_staging_url
       if template_url is None:
           raise RuntimeError("This platform does not provide a staging API 
endpoint.")
   
       return template_url
   ```
   
   #### `atr/shared/distribution.py` — `DistributionPlatform` (lines 78-102)
   _extension point_
   Does not include HEX/Erlang as @andrewmusselman noted; would need extension 
if new platforms are added as part of this investigation.
   
   ```python
   class DistributionPlatform(enum.Enum):
       """Wrapper enum for distribution platforms."""
   
       ARTIFACT_HUB = "Artifact Hub"
       DOCKER_HUB = "Docker Hub"
       MAVEN = "Maven Central"
       NPM = "npm"
       NPM_SCOPED = "npm (scoped)"
       PYPI = "PyPI"
   
       def to_sql(self) -> sql.DistributionPlatform:
           """Convert to SQL enum."""
           match self:
               case DistributionPlatform.ARTIFACT_HUB:
                   return sql.DistributionPlatform.ARTIFACT_HUB
               case DistributionPlatform.DOCKER_HUB:
                   return sql.DistributionPlatform.DOCKER_HUB
               case DistributionPlatform.MAVEN:
                   return sql.DistributionPlatform.MAVEN
               case DistributionPlatform.NPM:
                   return sql.DistributionPlatform.NPM
               case DistributionPlatform.NPM_SCOPED:
                   return sql.DistributionPlatform.NPM_SCOPED
               case DistributionPlatform.PYPI:
                   return sql.DistributionPlatform.PYPI
   ```
   
   #### `atr/storage/writers/distributions.py` — `CommitteeMember.record` 
(lines 143-173)
   _currently does this_
   The record method already calls __upgrade_staging_to_final when a production 
record is made for an existing staging record. This is the mechanism that would 
be augmented for remote promotion.
   
   ```python
       async def record(
           self,
           release_key: models.safe.ReleaseKey,
           platform: models.sql.DistributionPlatform,
           owner_namespace: models.safe.Alphanumeric | None,
           package: models.safe.Alphanumeric,
           version: models.safe.VersionKey,
           staging: bool,
           pending: bool,
           upload_date: datetime.datetime | None,
           api_url: str | None = None,
           web_url: str | None = None,
       ) -> tuple[models.sql.Distribution, bool]:
           namespace = str(owner_namespace) if owner_namespace else ""
           existing = await self.__data.distribution(
               str(release_key), platform, namespace, str(package), str(version)
           ).get()
           dist = models.sql.Distribution(
               platform=platform,
               release_key=str(release_key),
               owner_namespace=namespace,
               package=str(package),
               version=str(version),
               staging=staging,
               pending=pending,
               retries=0,
               upload_date=upload_date,
               api_url=api_url,
               web_url=web_url,
               created_by=self.__asf_uid,
           )
   ```
   
   #### `atr/models/distribution.py` — `Data` (lines 94-108)
   _extension point_
   @dave2wave identified this model class as needing extension based on 
investigation findings (e.g., promotion capabilities, staging revision 
tracking).
   
   ```python
   class Data(schema.Subset):
       platform: sql.DistributionPlatform
       owner_namespace: safe.Alphanumeric | None = None
       package: safe.Alphanumeric
       version: safe.VersionKey
       details: bool
   
       @pydantic.field_validator("owner_namespace", mode="before")
       @classmethod
       def empty_to_none(cls, v):
           if v is None:
               return None
           if isinstance(v, str) and (v.strip() == ""):
               return None
           return v
   ```
   
   #### `atr/tasks/distribution.py` — `status_check` (lines 30-66)
   _currently does this_
   Background task that retries pending distributions. If promotion were 
implemented, a similar task or extension would handle checking promotion status 
on remote platforms.
   
   ```python
   @checks.with_model(args.DistributionStatusCheckArgs)
   async def status_check(
       task_args: args.DistributionStatusCheckArgs, *, task_id: int | None = 
None
   ) -> results.Results | None:
       log.info("Checking pending recorded distributions")
       dists = []
       async with db.session() as data:
           dists = await data.distribution(
               pending=True, _with_release=True, _with_release_project=True, 
_limit=_BATCH_SIZE
           ).all()
       for dist in dists:
           name = f"{dist.platform} {dist.owner_namespace} {dist.package} 
{dist.version}"
           dd = dist.distribution_data()
           if not dist.created_by:
               log.warning(f"Distribution {name} has no creator, skipping")
               continue
           if not dist.release.project.committee_key:
               log.warning(f"Distribution {name} has no committee, skipping")
               continue
           try:
               async with 
storage.write_as_committee_member(dist.release.project.committee_key, 
dist.created_by) as w:
                   if dist.retries >= _RETRY_LIMIT:
                       await w.distributions.delete_distribution(
                           dist.safe_release_key, dist.platform, 
dist.owner_namespace, dist.package, dist.version
                       )
                       log.error(f"Distribution {name} failed {_RETRY_LIMIT} 
times, skipping")
                       continue
                   log.warning(f"Retrying distribution {name}")
                   await w.distributions.record_from_data(
                       dist.safe_release_key,
                       dist.staging,
                       dd,
                       allow_retries=True,
                   )
           except (distribution.DistributionError, storage.AccessError) as e:
               msg = f"Failed to record distribution: {e}"
               log.error(msg)
   ```
   
   ### Proposed approach
   This issue is an active investigation/discussion. The team has not yet 
converged on a final design. @andrewmusselman's latest RFC (May 1) proposes 
corrections to the investigation document and a narrower schema proposal, but 
this is still awaiting review from @sbp, @dave2wave, and @alitheg. No code 
changes should be proposed until the team agrees on: (1) which platforms will 
support promotion (currently leaning toward Docker Hub retagging, GitHub 
prerelease→release, and npm dist-tags as 'maybe'), (2) what schema changes are 
needed in `DistributionPlatformValue` (e.g., adding `supports_promotion`, 
`promotion_mechanism` fields), and (3) how revision tracking will be used to 
verify that promoted files match those voted on.
   
   The existing code already has the foundation for staging→production upgrades 
via `__upgrade_staging_to_final` in the distributions writer, but remote 
promotion would require new platform-specific API calls (e.g., Docker manifest 
retag, GitHub PATCH prerelease:false, npm dist-tag add) that don't yet exist. 
The investigation should conclude with a decision document before 
implementation begins.
   
   ### Open questions
   - Which platforms will the team ultimately decide to support promotion for? 
The investigation leans toward 'maybe' for Docker Hub, GitHub Releases, and npm.
   - What schema changes to DistributionPlatformValue in atr/models/sql.py are 
needed? @andrewmusselman's RFC proposes adding supports_promotion and 
promotion_mechanism fields but this hasn't been approved.
   - How will revision tracking be extended to verify that promoted artifacts 
match those voted on? The issue body identifies this as a necessary 
prerequisite.
   - Should Hex/Erlang be added as a new DistributionPlatform member as 
@dave2wave received an email about it?
   - Does the investigation output belong in atr/docs/ or elsewhere? 
@andrewmusselman raised this question.
   
   _The agent reviewed this issue and is not proposing patches in this run. 
Review the existing-code citations and open questions above before deciding 
next steps._
   
   ### Files examined
   - `atr/get/finish.py`
   - `atr/get/distribution.py`
   - `atr/models/distribution.py`
   - `atr/post/distribution.py`
   - `atr/post/finish.py`
   - `atr/shared/distribution.py`
   - `atr/storage/writers/distributions.py`
   - `atr/tasks/distribution.py`
   
   ---
   *Draft from a triage agent. A human reviewer should validate before merging 
any change. The agent did not run tests or verify diffs apply.*


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to