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

   <!-- gofannon-issue-triage-bot v2 -->
   
   **Automated triage** — analyzed at `main@837830e8`
   
   **Type:** `discussion`  •  **Classification:** `no_action`  •  
**Confidence:** `high`
   **Application domain(s):** `distribution_platforms`, `release_lifecycle`, 
`artifact_validation`
   
   ### Summary
   This issue is an investigation/RFC about whether ATR should support remote 
promotion of artifacts on third-party distribution platforms (e.g., promoting a 
Maven Central staging deployment to production) rather than re-uploading from 
ATR. The issue outlines platform-specific capabilities and caveats but does not 
request a specific code change yet. It identifies that tracking which revision 
was uploaded during staging would be needed, and that validation must ensure 
promoted files match voted-on artifacts.
   
   ### Where this lives in the code today
   
   #### `atr/storage/writers/distributions.py` — 
`CommitteeMember.__upgrade_staging_to_final` (lines 164-197)
   _currently does this_
   This method already handles upgrading a staging distribution record to 
final/production in the database, but it only updates the ATR record—it does 
not trigger any remote promotion API call on the platform itself.
   
   ```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/storage/writers/distributions.py` — `CommitteeMember.record` 
(lines 99-162)
   _extension point_
   The record method detects when a production record is being made for an 
existing staging record and calls __upgrade_staging_to_final. This is where 
remote promotion logic could be integrated in the future.
   
   ```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]:
           ...
           # If we're doing production and existing was for staging, upgrade it
           if (not staging) and existing.staging:
               upgraded = await self.__upgrade_staging_to_final(
                   release_key,
                   platform,
                   namespace,
                   str(package),
                   str(version),
                   pending,
                   upload_date,
                   api_url,
                   web_url,
               )
               ...
   ```
   
   #### `atr/storage/writers/distributions.py` — `CommitteeMember.automate` 
(lines 64-97)
   _extension point_
   The automate method creates distribution workflow tasks. It already accepts 
a revision_number parameter, which partially addresses the issue's requirement 
of tracking which revision was staged. A promotion workflow could be added 
alongside this.
   
   ```python
       async def automate(
           self,
           release_key: models.safe.ReleaseKey,
           platform: models.sql.DistributionPlatform,
           committee_key: str,
           owner_namespace: models.safe.Alphanumeric | None,
           project_key: models.safe.ProjectKey,
           version_key: models.safe.VersionKey,
           phase: str,
           revision_number: str | None,
           package: models.safe.Alphanumeric,
           version: models.safe.VersionKey,
           staging: bool,
       ) -> models.sql.Task:
   ```
   
   #### `atr/shared/distribution.py` — `DistributionPlatform` (lines 79-101)
   _currently does this_
   The supported distribution platforms are enumerated here. Remote promotion 
support would vary per platform as described in the issue (Maven supports it, 
PyPI/ArtifactHub don't, Docker/npm only with mutable tags).
   
   ```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"
   ```
   
   #### `atr/post/distribution.py` — `automate_form_process_page` (lines 43-100)
   _extension point_
   This is where automated distribution is triggered. A 'promote' action could 
be added here that, instead of re-uploading, calls a platform-specific 
promotion API (e.g., Maven Central's publish endpoint).
   
   ```python
   async def automate_form_process_page(
       session: web.Committer,
       form_data: shared.distribution.DistributionAutomateForm,
       project: safe.ProjectKey,
       version: safe.VersionKey,
       /,
       staging: bool = False,
   ) -> web.WerkzeugResponse:
       allowed_platforms = _AUTOMATED_PLATFORMS_STAGE if staging else 
_AUTOMATED_PLATFORMS
       if form_data.platform not in allowed_platforms:
           ...
       async with 
storage.write_as_committee_member(committee_key=committee.key) as w:
           try:
               await w.distributions.automate(
                   release.safe_key,
                   dd.platform,
                   committee.key,
                   dd.owner_namespace,
                   project,
                   version,
                   phase.value,
                   release.latest_revision_number,
                   dd.package,
                   dd.version,
                   staging,
               )
           ...
   ```
   
   #### `atr/tasks/distribution.py` — `status_check` (lines 32-69)
   _currently does this_
   The background task that retries pending distributions. Remote promotion 
would likely integrate into this retry/status-check loop if promotion is 
asynchronous.
   
   ```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:
           ...
   ```
   
   ### Where new code would go
   - `atr/storage/writers/distributions.py` — after symbol 
CommitteeMember.__upgrade_staging_to_final
     A new method like `promote_staging_to_production` would live here, calling 
platform-specific promotion APIs (e.g., Maven Central's publish endpoint) after 
validating that the staged revision matches the voted-on revision.
   - `atr/shared/distribution.py` — after symbol DistributionPlatform
     A property or method on DistributionPlatform indicating whether the 
platform supports remote promotion would be needed to gate the feature.
   - `atr/tasks/checks/` — new file
     A new check that validates staged artifacts match voted-on artifacts 
(revision tracking) could be implemented as a task check.
   
   ### Proposed approach
   This issue is an investigation/discussion rather than a concrete feature 
request. It outlines the conceptual requirements for remote promotion: (1) 
track which file revision was uploaded to staging, (2) validate that those 
files haven't changed in subsequent revisions before allowing promotion, (3) 
implement platform-specific promotion APIs where supported (currently only 
Maven Central clearly supports this). The existing code already has some 
infrastructure for this: `automate()` stores `revision_number`, and 
`__upgrade_staging_to_final()` handles the database-side staging→production 
transition. However, significant new work would be needed: per-platform 
promotion API calls, revision-to-file hash tracking, and validation logic 
comparing staged vs. voted-on artifacts. No concrete patch is appropriate at 
this stage since the issue explicitly frames this as investigation.
   
   ### Open questions
   - Which platforms should be prioritized for remote promotion support? The 
issue suggests Maven Central is the clearest candidate.
   - How should revision tracking be implemented? The `revision_number` field 
exists on Task but there's no explicit record of which file hashes were staged 
to a platform.
   - Should a new SQL model be created to track per-file-per-platform staging 
state (file hash, revision number, platform deployment ID)?
   - For platforms with mutable tags (Docker Hub, npm), is promotion via tag 
reassignment acceptable from a supply chain security perspective?
   - Does this feature need to be gated behind a release policy setting so 
projects can opt in/out?
   
   _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/shared/distribution.py`
   - `atr/storage/writers/distributions.py`
   - `atr/tasks/distribution.py`
   - `atr/get/distribution.py`
   - `atr/models/distribution.py`
   - `atr/post/distribution.py`
   - `atr/get/finish.py`
   - `atr/post/finish.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