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

   <!-- gofannon-issue-triage-bot v2 -->
   
   **Automated triage** — analyzed at `main@2da7807a`
   
   **Type:** `new_feature`  •  **Classification:** `actionable`  •  
**Confidence:** `high`
   **Application domain(s):** `project_committee_management`, 
`shared_infrastructure`
   
   ### Summary
   Issue #912 describes a project cycle model (lifecycle tracking for version 
lines) and project-level version metadata. The core implementation already 
exists in the codebase: migration 0074 creates the `projectcycle` table and 
adds version metadata fields to `project` and `cycle_key` to `release`; 
`atr/cycles.py` implements cycle resolution; `atr/models/sql.py` defines 
`VersionMethod` enum and the `ProjectCycle` relationship on `Project`; and 
`atr/db/__init__.py` provides a `project_cycle()` query builder. Unit tests 
exist. The data model described in the issue is fully implemented. Remaining 
work may include UI forms for configuring version settings, automatic cycle 
creation when releases are drafted, and EOD/EOS/EOL lifecycle automation.
   
   ### Where this lives in the code today
   
   #### `migrations/versions/0074_2026.05.04_0d6e9554.py` — `upgrade` (lines 
22-59)
   _currently does this_
   Database migration creating the projectcycle table and adding version 
metadata columns to project/release tables, implementing the schema specified 
in issue #912.
   
   ```python
   def upgrade() -> None:
       # project: add version-scheme metadata. Existing rows default to 
"simple".
       with op.batch_alter_table("project", schema=None) as batch_op:
           batch_op.add_column(
               sa.Column(
                   "version_method",
                   sa.Enum("SIMPLE", "SEMVER", "CALVER", name="versionmethod"),
                   nullable=False,
                   server_default="SIMPLE",
               )
           )
           batch_op.add_column(sa.Column("version_pattern", sa.String(), 
nullable=True))
           batch_op.add_column(sa.Column("cycle_match", sa.String(), 
nullable=True))
           batch_op.add_column(sa.Column("branch_template", sa.String(), 
nullable=True))
   
       # projectcycle: create the table and backfill a "default" cycle for 
every project.
       op.create_table(
           "projectcycle",
           sa.Column("cycle_key", sa.String(), nullable=False),
           sa.Column("cycle", sa.String(), nullable=False),
           sa.Column("project_key", sa.String(), nullable=False),
           sa.Column("start", sql.UTCDateTime(timezone=True), nullable=True),
           sa.Column("begin", sql.UTCDateTime(timezone=True), nullable=True),
           sa.Column("latest", sql.UTCDateTime(timezone=True), nullable=True),
           sa.Column("eod", sql.UTCDateTime(timezone=True), nullable=True),
           sa.Column("eos", sql.UTCDateTime(timezone=True), nullable=True),
           sa.Column("eol", sql.UTCDateTime(timezone=True), nullable=True),
           sa.Column("lts", sa.Boolean(), nullable=False, 
server_default=sa.text("0")),
           sa.ForeignKeyConstraint(
               ["project_key"],
               ["project.key"],
               name=op.f("fk_projectcycle_project_key_project"),
               ondelete="CASCADE",
           ),
           sa.PrimaryKeyConstraint("cycle_key", name=op.f("pk_projectcycle")),
           sa.UniqueConstraint("cycle_key", 
name=op.f("uq_projectcycle_cycle_key")),
           sa.UniqueConstraint("project_key", "cycle", 
name="unique_project_cycle"),
       )
   ```
   
   #### `atr/cycles.py` — `cycle_name_for_version` (lines 37-61)
   _currently does this_
   Core cycle resolution logic that maps a version string to a cycle name using 
the project's cycle_match regex pattern.
   
   ```python
   def cycle_name_for_version(project: sql.Project, version: str) -> str:
       """Resolve which cycle a version belongs to.
   
       Returns the cycle name (not the FK key). Projects with no `cycle_match`
       set always return "default". Otherwise the regex is fullmatched against
       the version string and capture-group 1 is returned.
   
       Raises ValueError if the version doesn't match, the pattern has no
       capture groups, or capture-group 1 captured the empty string.
       """
       if project.cycle_match is None:
           return _DEFAULT_CYCLE
   
       match = re.fullmatch(project.cycle_match, version)
       if match is None:
           raise ValueError(f"Version {version!r} does not match cycle_match 
for project {project.key!r}")
   
       if not match.groups():
           raise ValueError(f"cycle_match for project {project.key!r} has no 
capture groups")
   
       cycle = match.group(1)
       if not cycle:
           raise ValueError(f"cycle_match for project {project.key!r} captured 
empty string from version {version!r}")
   
       return cycle
   ```
   
   #### `atr/models/sql.py` — `VersionMethod` (lines 244-247)
   _currently does this_
   Enum for the version_method field on Project, implementing the 'method' 
column from the issue specification (semver, calver, simple).
   
   ```python
   class VersionMethod(enum.StrEnum):
       SIMPLE = "simple"
       SEMVER = "semver"
       CALVER = "calver"
   ```
   
   #### `atr/models/sql.py` — `LifecycleEventType` (lines 262-268)
   _currently does this_
   Enum for lifecycle events matching the eod/eos/eol fields described in the 
issue's cycle model.
   
   ```python
   class LifecycleEventType(enum.StrEnum):
       RELEASE = "release"
       ARCHIVE = "archive"
       WITHDRAW = "withdraw"
       EOD = "eod"
       EOS = "eos"
       EOL = "eol"
   ```
   
   #### `atr/db/__init__.py` — `Session.project_cycle` (lines 432-454)
   _currently does this_
   Query builder for ProjectCycle, enabling database lookups by cycle_key, 
project_key, and cycle name with eager-loading options.
   
   ```python
       def project_cycle(
           self,
           cycle_key: Opt[str] = NOT_SET,
           project_key: Opt[str] = NOT_SET,
           cycle: Opt[str] = NOT_SET,
           _project: bool = False,
           _releases: bool = False,
       ) -> Query[sql.ProjectCycle]:
           query = sqlmodel.select(sql.ProjectCycle)
   
           if is_defined(cycle_key):
               query = query.where(sql.ProjectCycle.cycle_key == cycle_key)
           if is_defined(project_key):
               query = query.where(sql.ProjectCycle.project_key == project_key)
           if is_defined(cycle):
               query = query.where(sql.ProjectCycle.cycle == cycle)
   
           if _project:
               query = query.options(joined_load(sql.ProjectCycle.project))
           if _releases:
               query = query.options(select_in_load(sql.ProjectCycle.releases))
   
           return Query(self, query)
   ```
   
   #### `tests/unit/test_cycles.py` — 
`test_cycle_name_extracts_capture_group_for_matching_version` (lines 30-32)
   _currently does this_
   Unit test verifying semver-style cycle extraction from version strings, 
confirming the cycle resolution logic works as specified.
   
   ```python
   def test_cycle_name_extracts_capture_group_for_matching_version():
       project = SimpleNamespace(key="example", 
cycle_match=r"^(\d+)\.\d+\.\d+$")
       assert cycles.cycle_name_for_version(project, "2.5.3") == "2"
   ```
   
   ### Where new code would go
   - `atr/get/cycles.py` — new file
     A UI view for displaying and configuring project cycles does not appear to 
exist yet. A GET route handler for viewing cycle settings would go here.
   - `atr/post/cycles.py` — new file
     A POST route handler for updating project version method, pattern, 
cycle_match, and branch_template would go here (form submission to configure 
cycles).
   
   ### Proposed approach
   The data model for issue #912 is already fully implemented: the migration 
creates the `projectcycle` table with all specified fields (cycle_key, cycle, 
project_key, start, begin, latest, eod, eos, eol, lts), adds version metadata 
to `project` (version_method, version_pattern, cycle_match, branch_template), 
and links releases to cycles via `cycle_key`. The cycle resolution logic in 
`atr/cycles.py` is tested and functional.
   
   Remaining work likely includes: (1) UI forms for PMC members to configure 
version_method/pattern/cycle_match/branch_template on their projects, (2) 
automatic cycle creation when a release is drafted with a new cycle name 
derived from cycle_match, (3) logic to update ProjectCycle.latest/start/begin 
timestamps when releases progress through phases, and (4) EOD/EOS/EOL lifecycle 
automation (archiving older releases when a cycle reaches end-of-life). Since 
the core model is in place and the issue has no comments specifying further 
work, additional diffs are speculative.
   
   ### Open questions
   - Is the issue considered complete now that the data model and migration 
exist, or are there additional UI/automation pieces being tracked under this 
same issue?
   - Should automatic cycle creation happen during release drafting (i.e., when 
a version is first entered and cycle_name_for_version resolves a new cycle 
name)?
   - How should the eod/eos/eol datetime fields interact with release archival 
- is there a scheduled task or is it triggered by a PMC member setting the date?
   - The ProjectCycle model class definition is not visible in the truncated 
sql.py file - it should exist there with fields matching the migration, but 
confirmation would be good.
   
   ### Files examined
   - `migrations/versions/0074_2026.05.04_0d6e9554.py`
   - `atr/cycles.py`
   - `atr/models/sql.py`
   - `atr/db/__init__.py`
   - `tests/unit/test_cycles.py`
   - `atr/db/interaction.py`
   - `atr/datasources/apache.py`
   - `atr/registry.py`
   
   ### Related issues
   This issue appears related to: #914.
   
   _Both define project cycle and lifecycle event models for tracking release 
information_
   
   ---
   *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