You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tooling.apache.org by "dave2wave (via GitHub)" <gi...@apache.org> on 2026/03/19 20:25:02 UTC

[I] Reorganize committee page (tooling-trusted-releases)

dave2wave opened a new issue, #920:
URL: https://github.com/apache/tooling-trusted-releases/issues/920

   The committee page should be reorganized into sections like the project page in #917 
   
   1. Projects. Should become a list of project tiles like on committees page. Also, add a create a new project button if the user is a PMC member.
   2. Signing keys.
       - Sort the table by asf_uid then fingerprint.
       - Add a column to show how many current artifacts are signed with that key (see #911 )
       - Add a column to indicate if the key is expired.
       - Add a column to indicate if the user is currently a member or committer of the committee.
   3. Roster. Instead of a count of members and committers show a roster sorted by ASF UID of
       - ASF UID 
       - Name
       - PMC Member check box
       - Artifact signature check box (or count)


-- 
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: dev-unsubscribe@tooling.apache.org.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tooling.apache.org
For additional commands, e-mail: dev-help@tooling.apache.org


Re: [I] Reorganize committee page (tooling-trusted-releases)

Posted by "alitheg (via GitHub)" <gi...@apache.org>.
alitheg closed issue #920: Reorganize committee page
URL: https://github.com/apache/tooling-trusted-releases/issues/920


-- 
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: dev-unsubscribe@tooling.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tooling.apache.org
For additional commands, e-mail: dev-help@tooling.apache.org


Re: [I] Reorganize committee page (tooling-trusted-releases)

Posted by "asf-tooling (via GitHub)" <gi...@apache.org>.
asf-tooling commented on issue #920:
URL: https://github.com/apache/tooling-trusted-releases/issues/920#issuecomment-4410023899

   <!-- gofannon-issue-triage-bot v2 -->
   
   **Automated triage** — analyzed at `main@2da7807a`
   
   **Type:** `new_feature`  •  **Classification:** `actionable`  •  **Confidence:** `medium`
   **Application domain(s):** `project_committee_management`, `cryptographic_keys`
   
   ### Summary
   Issue #920 requests reorganizing the committee page into sections (Projects tiles, enhanced Signing Keys table, Roster) similar to the project page. The signing keys table should be sorted by asf_uid then fingerprint, and gain columns for artifact signature count, key expiration, and member/committer status. @dave2wave noted the same signing keys table changes should apply to the Keys page. The committee view uses a Jinja template (committee-view.html) not visible in the provided source, but the keys page has a programmatic table in `_committee_keys` that can be patched directly.
   
   ### Where this lives in the code today
   
   #### `atr/get/committees.py` — `view` (lines 35-70)
   _needs modification_
   The committee view function passes data to a Jinja template; needs additional roster data and project add URL.
   
   ```python
   @get.typed
   async def view(session: web.Public, _committees: Literal["committees"], name: safe.CommitteeKey) -> str:
       """
       URL: /committees/<name>
       """
       # TODO: Could also import this from keys.py
       async with db.session() as data:
           committee = await data.committee(
               key=str(name),
               _projects=True,
               _public_signing_keys=True,
           ).demand(base.ASFQuartException(f"Committee {name!s} not found", errorcode=404))
       project_list = list(committee.projects)
       committee_member = False
       if isinstance(session, web.Committer):
           committee_member = await session.prevent_confusing_ui_display_committee(name, False)
       for project in project_list:
           # Workaround for the usual loading problem
           project.committee = committee
       return await template.render(
           "committee-view.html",
           committee=committee,
           projects=project_list,
           algorithms=shared.algorithms,
           now=datetime.datetime.now(datetime.UTC),
           email_from_key=util.email_from_uid,
           is_committee_member=committee_member,
           update_committee_keys_form=await form.render(
               model_cls=shared.keys.UpdateCommitteeKeysForm,
               action=util.as_url(post.keys.keys),
               submit_label="Regenerate KEYS file",
               defaults={"committee_key": committee.key},
               empty=True,
           ),
           is_standing=util.committee_is_standing(committee.key),
       )
   ```
   
   #### `atr/get/keys.py` — `_committee_keys` (lines 281-309)
   _needs modification_
   The committee keys table on the keys page needs sorting by asf_uid then fingerprint, plus new columns for expiration and member status per @dave2wave's comment.
   
   ```python
   async def _committee_keys(page: htm.Block, user_committees_with_keys: list[sql.Committee]) -> None:
       page.h2("#your-committee-keys")["Your committee's keys"]
       page.div(".mb-4")[htm.a(".btn.btn-outline-primary", href=util.as_url(upload))["Upload a KEYS file"]]
   
       for committee in user_committees_with_keys:
           if not util.committee_is_standing(committee.key):
               page.h3(f"#committee-{committee.key}.mt-3")[committee.display_name or committee.key]
   
               if committee.public_signing_keys:
                   thead = htm.thead[
                       htm.tr[
                           htm.th(".px-2", scope="col")["Key ID"],
                           htm.th(".px-2", scope="col")["Email"],
                           htm.th(".px-2", scope="col")["Apache UID"],
                       ]
                   ]
                   tbody = htm.Block(htm.tbody)
                   for key in committee.public_signing_keys:
                       row = htm.Block(htm.tr)
                       details_url = util.as_url(details, fingerprint=key.fingerprint)
                       row.td(".text-break.font-monospace.px-2")[htm.a(href=details_url)[key.fingerprint[-16:].upper()]]
                       email = util.email_from_uid(key.primary_declared_uid) if key.primary_declared_uid else "-"
                       row.td(".text-break.px-2")[email or "-"]
                       row.td(".text-break.px-2")[key.apache_uid or "-"]
                       tbody.append(row.collect())
   
                   page.div(".table-responsive.mb-2")[
                       htm.table(".table.border.table-striped.table-sm")[thead, tbody.collect()]
                   ]
   ```
   
   ### Where new code would go
   - `atr/get/keys.py` — after symbol _committee_keys
     A helper function to compute key expiration badge for table cells would reduce duplication between the details page and the new table columns.
   - `atr/get/committees.py` — after symbol view
     A helper to build the roster data (members/committers with their signing key status) for passing to the committee template.
   
   ### Proposed approach
   The implementation should be split into two main areas:
   
   1. **Keys page table enhancement** (`atr/get/keys.py`): Modify `_committee_keys` to sort keys by `apache_uid` then `fingerprint`, add an 'Expired' column using the key's `expires` field, and add a 'Status' column showing whether the key owner is currently a member/committer (using `committee.committee_members` and `committee.committers`). The artifact signature count column (from #911) likely requires a new DB query counting release artifacts signed by each key; this may need to be deferred until #911 is resolved.
   
   2. **Committee page reorganization** (`atr/get/committees.py` and the `committee-view.html` template): The view function needs to pass roster data (committee_members + committers with names) and a project-add URL. Since the template isn't available in the source provided, the Python-side changes focus on preparing the data. The template would need restructuring into card sections matching the project page style.
   
   A shared helper for rendering expiration badges would reduce duplication between the key details page and the new table columns.
   
   ### Suggested patches
   
   #### `atr/get/keys.py`
   Sort committee keys by asf_uid then fingerprint, add Expired and Member columns to the committee keys table per issue requirements and @dave2wave's comment.
   
   ````diff
   --- a/atr/get/keys.py
   +++ b/atr/get/keys.py
   @@ -238,6 +238,18 @@ async def upload(_session: web.Committer, _keys_upload: Literal["keys/upload"])
        return await shared.keys.render_upload_page()
    
    
   +def _expiration_badge(key: sql.PublicSigningKey) -> htm.Element | str:
   +    """Return an expiration badge element or empty string."""
   +    if not key.expires:
   +        return "-"
   +    now = datetime.datetime.now(datetime.UTC)
   +    days_until_expiry = (key.expires - now).days
   +    if days_until_expiry < 0:
   +        return htm.span(".badge.bg-danger.text-white")["Expired"]
   +    elif days_until_expiry <= 30:
   +        return htm.span(".badge.bg-warning.text-dark")[f"{days_until_expiry}d"]
   +    return htm.span(".text-muted")[key.expires.strftime("%Y-%m-%d")]
   +
   +
    async def _committee_keys(page: htm.Block, user_committees_with_keys: list[sql.Committee]) -> None:
        page.h2("#your-committee-keys")["Your committee's keys"]
        page.div(".mb-4")[htm.a(".btn.btn-outline-primary", href=util.as_url(upload))["Upload a KEYS file"]]
   @@ -247,21 +259,35 @@ async def _committee_keys(page: htm.Block, user_committees_with_keys: list[sql.C
                page.h3(f"#committee-{committee.key}.mt-3")[committee.display_name or committee.key]
    
                if committee.public_signing_keys:
   +                # Sort by apache_uid then fingerprint
   +                sorted_keys = sorted(
   +                    committee.public_signing_keys,
   +                    key=lambda k: (k.apache_uid or "", k.fingerprint),
   +                )
   +
                    thead = htm.thead[
                        htm.tr[
                            htm.th(".px-2", scope="col")["Key ID"],
                            htm.th(".px-2", scope="col")["Email"],
                            htm.th(".px-2", scope="col")["Apache UID"],
   +                        htm.th(".px-2", scope="col")["Expires"],
   +                        htm.th(".px-2", scope="col")["Status"],
                        ]
                    ]
                    tbody = htm.Block(htm.tbody)
   -                for key in committee.public_signing_keys:
   +                members_set = set(getattr(committee, "committee_members", []))
   +                committers_set = set(getattr(committee, "committers", []))
   +                for key in sorted_keys:
                        row = htm.Block(htm.tr)
                        details_url = util.as_url(details, fingerprint=key.fingerprint)
                        row.td(".text-break.font-monospace.px-2")[htm.a(href=details_url)[key.fingerprint[-16:].upper()]]
                        email = util.email_from_uid(key.primary_declared_uid) if key.primary_declared_uid else "-"
                        row.td(".text-break.px-2")[email or "-"]
                        row.td(".text-break.px-2")[key.apache_uid or "-"]
   +                    row.td(".text-break.px-2")[_expiration_badge(key)]
   +                    uid = (key.apache_uid or "").lower()
   +                    status = "PMC" if uid in members_set else ("Committer" if uid in committers_set else "-")
   +                    row.td(".text-break.px-2")[status]
                        tbody.append(row.collect())
    
                    page.div(".table-responsive.mb-2")[
   ````
   
   #### `atr/get/committees.py`
   Pass roster data and add-project URL to the committee view template for the new Roster section and Projects tile with create button.
   
   ````diff
   --- a/atr/get/committees.py
   +++ b/atr/get/committees.py
   @@ -46,6 +46,8 @@ async def view(session: web.Public, _committees: Literal["committees"], name: sa
        """
        URL: /committees/<name>
        """
   +    import atr.get.projects as projects
   +
        # TODO: Could also import this from keys.py
        async with db.session() as data:
            committee = await data.committee(
   @@ -64,6 +66,8 @@ async def view(session: web.Public, _committees: Literal["committees"], name: sa
            "committee-view.html",
            committee=committee,
            projects=project_list,
   +        add_project_url=util.as_url(projects.add_project, committee_key=committee.key) if committee_member else None,
   +        roster=_build_roster(committee),
            algorithms=shared.algorithms,
            now=datetime.datetime.now(datetime.UTC),
            email_from_key=util.email_from_uid,
   @@ -77,3 +81,23 @@ async def view(session: web.Public, _committees: Literal["committees"], name: sa
            ),
            is_standing=util.committee_is_standing(committee.key),
        )
   +
   +
   +def _build_roster(committee: sql.Committee) -> list[dict[str, object]]:
   +    """Build a roster of members and committers with their roles."""
   +    members = set(getattr(committee, "committee_members", []))
   +    committers = set(getattr(committee, "committers", []))
   +    all_uids = sorted(members | committers)
   +
   +    # Build set of UIDs that have signing keys
   +    signing_key_uids: set[str] = set()
   +    if hasattr(committee, "public_signing_keys") and committee.public_signing_keys:
   +        for key in committee.public_signing_keys:
   +            if key.apache_uid:
   +                signing_key_uids.add(key.apache_uid.lower())
   +
   +    return [
   +        {"uid": uid, "is_pmc_member": uid in members, "has_signing_key": uid in signing_key_uids}
   +        for uid in all_uids
   +    ]
   ````
   
   ### Open questions
   - The committee view uses a Jinja template (committee-view.html) not provided in the source - the actual UI restructuring (tiles, roster table, card sections) must happen there.
   - The artifact signature count column (referenced from #911) requires a database query to count release artifacts signed with each key - the implementation depends on how #911 is resolved.
   - The roster section asks for 'Name' but ASF display names may come from LDAP cache - need to confirm how to look up display names from UIDs.
   - The committee model attributes committee_members and committers may need to be eagerly loaded (via _committee_members flag or similar) in the view query.
   - Should the 'Status' column in the keys table show member/committer status at the committee level or at the project level?
   
   ### Files examined
   - `atr/get/committees.py`
   - `atr/get/keys.py`
   - `atr/shared/keys.py`
   - `atr/get/projects.py`
   - `atr/post/keys.py`
   - `atr/post/projects.py`
   - `atr/shared/projects.py`
   - `atr/storage/writers/keys.py`
   
   ### Related issues
   This issue appears related to: #917.
   
   _Both address reorganization of project and committee pages with improved navigation and sections_
   
   ---
   *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: dev-unsubscribe@tooling.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tooling.apache.org
For additional commands, e-mail: dev-help@tooling.apache.org


Re: [I] Reorganize committee page (tooling-trusted-releases)

Posted by "dave2wave (via GitHub)" <gi...@apache.org>.
dave2wave commented on issue #920:
URL: https://github.com/apache/tooling-trusted-releases/issues/920#issuecomment-4093116978

   Apply these same signing keys table changes to the Keys page.


-- 
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: dev-unsubscribe@tooling.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tooling.apache.org
For additional commands, e-mail: dev-help@tooling.apache.org


Re: [I] Reorganize committee page (tooling-trusted-releases)

Posted by "alitheg (via GitHub)" <gi...@apache.org>.
alitheg commented on issue #920:
URL: https://github.com/apache/tooling-trusted-releases/issues/920#issuecomment-4442200588

   Redesign completed but the remainder is pending #911 


-- 
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: dev-unsubscribe@tooling.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tooling.apache.org
For additional commands, e-mail: dev-help@tooling.apache.org