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

   <!-- gofannon-issue-triage-bot v2 -->
   
   **Automated triage** — analyzed at `main@ab610b23`
   
   **Type:** `discussion`  •  **Classification:** `no_action`  •  
**Confidence:** `high`
   **Application domain(s):** `distribution_and_publishing`, 
`notification_and_messaging`, `release_workflow`
   
   ### Summary
   This issue requests per-phase configurable mailing recipients in the release 
workflow (vote, vote results, announcement). Currently the announce step 
already allows recipient selection (email_to, email_cc, email_bcc in 
AnnounceForm), but the vote-results step apparently doesn't have independent 
recipient configuration. @dave2wave explicitly stated 'Let's discuss this later 
this week', indicating the team wants to discuss the approach before any 
implementation work begins. No diffs should be proposed at this stage.
   
   ### Where this lives in the code today
   
   #### `atr/shared/announce.py` — `AnnounceForm` (lines 27-48)
   _currently does this_
   The announce form already has email_to, email_cc, email_bcc fields showing 
recipient configuration is supported at the announcement phase.
   
   ```python
   class AnnounceForm(form.Form):
       """Form for announcing a release preview."""
   
       revision_number: safe.RevisionNumber = form.label("Revision number", 
widget=form.Widget.HIDDEN)
       email_to: str = form.label("To", widget=form.Widget.CUSTOM)
       email_cc: form.StrList = form.label("CC")
       email_bcc: form.StrList = form.label("BCC")
       subject: str = form.label("Subject", widget=form.Widget.CUSTOM)
       subject_template_hash: str = form.label("Subject template hash", 
widget=form.Widget.HIDDEN)
       body: str = form.label("Body", widget=form.Widget.CUSTOM, 
max_length=100_000)
       download_path_suffix: safe.OptionalRelPath = form.label("Download path 
suffix", widget=form.Widget.CUSTOM)
       auto_archive: form.Bool = form.label(
           "Auto archive prior release",
           "If set, the release shown below will be auto-archived",
           widget=form.Widget.CHECKBOX,
           default=False,
       )
       auto_archive_release: str = form.label("Version to archive", 
widget=form.Widget.STATIC, default="")
       confirm_announce: Literal["CONFIRM"] = form.label(
           "Confirm",
           "Type CONFIRM (in capitals) to enable the submit button.",
       )
   ```
   
   #### `atr/storage/writers/announce.py` — `CommitteeMember.release` (lines 
106-123)
   _currently does this_
   The writer that actually sends the announcement email accepts recipient 
parameters, validating them against permitted recipients.
   
   ```python
       async def release(  # noqa: C901
           self,
           project_key: safe.ProjectKey,
           version_key: safe.VersionKey,
           preview_revision_number: safe.RevisionNumber,
           email_to: str,
           body: str,
           download_path_suffix: safe.RelPath | None,
           fullname: str,
           subject_template_hash: str | None = None,
           email_cc: list[str] | None = None,
           email_bcc: list[str] | None = None,
       ) -> None:
           permitted = util.permitted_announce_recipients(self.__asf_uid)
           all_addrs = [email_to] + (email_cc or []) + (email_bcc or [])
           for addr in all_addrs:
               if addr not in permitted:
                   raise storage.AccessError(f"You are not permitted to send 
announcements to {addr}", status=403)
   ```
   
   #### `atr/construct.py` — `StartVoteOptions` (lines 128-135)
   _currently does this_
   The vote start options dataclass shows the vote initiation path exists but 
doesn't include recipient configuration in the template options (recipients are 
handled separately in the vote start form).
   
   ```python
   @dataclasses.dataclass
   class StartVoteOptions:
       asfuid: str
       fullname: str
       project_key: safe.ProjectKey
       version_key: safe.VersionKey
       revision_number: safe.RevisionNumber
       vote_duration: int
   ```
   
   #### `atr/tasks/message.py` — `send` (lines 30-53)
   _currently does this_
   The message send task handles all email sending via the task queue, 
enforcing that recipients must be apache.org addresses. Any new per-phase 
recipient configuration would ultimately go through this path.
   
   ```python
   @checks.with_model(args.Send)
   async def send(task_args: args.Send) -> results.Results | None:
       if "@" not in task_args.email_sender:
           log.warning(f"Invalid email sender: {task_args.email_sender}")
           sender_asf_uid = task_args.email_sender
       elif task_args.email_sender.endswith("@apache.org"):
           sender_asf_uid = task_args.email_sender.split("@")[0]
       else:
           raise SendError(f"Invalid email sender: {task_args.email_sender}")
   
       sender_account = await ldap.account_lookup(sender_asf_uid)
       if sender_account is None:
           raise SendError(f"Invalid email account: {task_args.email_sender}")
       if ldap.is_banned(sender_account):
           raise SendError(f"Email account {task_args.email_sender} is banned")
   
       all_recipients = [task_args.email_to, *task_args.email_cc, 
*task_args.email_bcc]
       for addr in all_recipients:
           recipient_domain = addr.split("@")[-1]
           sending_to_self = addr == f"{sender_asf_uid}@apache.org"
           # audit_guidance we intentionally allow users to send messages to 
committees they are not a part of
           sending_to_committee = recipient_domain.endswith(".apache.org")
           if not (sending_to_self or sending_to_committee):
               raise SendError(f"You are not permitted to send emails to 
{addr}")
   ```
   
   ### Where new code would go
   - `atr/models/sql.py` — after symbol ReleasePolicy or ProjectReleasePolicy
     Per-phase default recipients would likely be stored as project/release 
policy fields (e.g., policy_vote_recipients, policy_result_recipients, 
policy_announce_recipients).
   - `atr/shared/vote_result.py or atr/shared/vote.py` — new file or existing 
vote form
     A form for vote results would need recipient fields similar to 
AnnounceForm if the vote-result step gets configurable recipients.
   
   ### Proposed approach
   This issue is in early discussion phase — @dave2wave requested a team 
discussion later in the week. The feature would involve: (1) adding per-phase 
default recipient configuration to the project or release policy model in the 
database, (2) presenting those defaults in each phase's form (vote start, vote 
result, announcement), and (3) allowing users to adjust recipients at each step 
independently. The announce step already supports this via AnnounceForm; the 
vote-start step likely does too. The main gap appears to be the vote-results 
step and possibly storing project-level defaults for each phase.
   
   Since the team has explicitly deferred this to a design discussion, no 
implementation should be proposed at this time.
   
   ### Open questions
   - What is the current recipient handling for the vote-results email step? 
(The vote result sender code was not in the provided files.)
   - Should per-phase defaults be stored at the project level (in 
ProjectReleasePolicy) or the release level?
   - Should each phase have a 'permitted recipients' list that differs from the 
other phases, or should all phases share the same permitted list with different 
defaults?
   - How does this relate to referenced issue #826?
   - The team discussion hasn't happened yet — any implementation should wait 
for the design conversation @dave2wave proposed.
   
   _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/construct.py`
   - `atr/post/announce.py`
   - `atr/shared/announce.py`
   - `atr/storage/writers/announce.py`
   - `atr/get/announce.py`
   - `atr/mail.py`
   - `atr/storage/writers/mail.py`
   - `atr/tasks/message.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