diff --git a/component-config.yaml.sample b/component-config.yaml.sample index 8afc90a..9f7562b 100644 --- a/component-config.yaml.sample +++ b/component-config.yaml.sample @@ -38,3 +38,12 @@ issues: id: subtask_rpminspect parent_id: errata_task on_respin: close + + - summary: "regression testing" + description: "Run automated tests" + assignee: '{{ ERRATUM.people_assigned_to }}' + type: subtask + id: subtask_regression + parent_id: errata_task + on_respin: close + job_recipe: https://path/to/recipe.yaml diff --git a/newa/__init__.py b/newa/__init__.py index b2a0da4..75936af 100644 --- a/newa/__init__.py +++ b/newa/__init__.py @@ -148,7 +148,27 @@ class Erratum(Cloneable, Serializable): """ An eratum """ release: str - # builds: list[...] = ... + builds: list[str] = field(factory=list) + + def fetch_details(self) -> None: + raise NotImplementedError + + +@define +class Issue(Cloneable, Serializable): + """ A Jira issue """ + + id: str + + def fetch_details(self) -> None: + raise NotImplementedError + + +@define +class Recipe(Cloneable, Serializable): + """ A job recipe """ + + url: str def fetch_details(self) -> None: raise NotImplementedError @@ -162,7 +182,7 @@ class Job(Cloneable, Serializable): converter=lambda x: x if isinstance(x, Event) else Event(**x), ) - # issue: ... + # jira: ... # recipe: ... # test_job: ... # job_result: ... @@ -185,9 +205,28 @@ def id(self) -> str: return f'{self.event.id} @ {self.erratum.release}' +@define +class JiraJob(ErratumJob): + """ A single *jira* job """ + + jira: Issue = field( # type: ignore[var-annotated] + converter=lambda x: x if isinstance(x, Issue) else Issue(**x), + ) + + recipe: Recipe = field( # type: ignore[var-annotated] + converter=lambda x: x if isinstance(x, Recipe) else Recipe(**x), + ) + + @property + def id(self) -> str: + return f'{self.event.id} @ {self.erratum.release}' + + # # Component configuration # + + class IssueType(Enum): EPIC = 'epic' TASK = 'task' @@ -210,6 +249,7 @@ class IssueAction: # type: ignore[no-untyped-def] converter=lambda value: OnRespinAction(value) if value else None) type: IssueType = field(converter=IssueType) parent_id: Optional[str] = None + job_recipe: Optional[str] = None @define diff --git a/newa/cli.py b/newa/cli.py index 771f81f..9307626 100644 --- a/newa/cli.py +++ b/newa/cli.py @@ -1,3 +1,4 @@ +import itertools import logging import os.path from collections.abc import Iterable, Iterator @@ -7,7 +8,18 @@ import click from attrs import define -from . import Erratum, ErratumConfig, ErratumJob, Event, EventType, InitialErratum, render_template +from . import ( + Erratum, + ErratumConfig, + ErratumJob, + Event, + EventType, + InitialErratum, + Issue, + JiraJob, + Recipe, + render_template, + ) logging.basicConfig( format='%(asctime)s %(message)s', @@ -68,6 +80,13 @@ def save_erratum_jobs(self, filename_prefix: str, jobs: Iterable[ErratumJob]) -> for job in jobs: self.save_erratum_job(filename_prefix, job) + def save_jira_job(self, filename_prefix: str, job: JiraJob) -> None: + filepath = self.state_dirpath / \ + f'{filename_prefix}{job.event.id}-{job.erratum.release}-{job.jira.id}.yaml' + + job.to_yaml_file(filepath) + self.logger.info(f'Jira job {job.id} written to {filepath}') + @click.group(chain=True) @click.option( @@ -124,6 +143,9 @@ def cmd_event(ctx: CLIContext, errata_ids: tuple[str, ...]) -> None: def cmd_jira(ctx: CLIContext) -> None: ctx.enter_command('jira') + # this is here temporarily so we generate fake Jira issue IDs + jira_id_gen = itertools.count(start=1) + for erratum_job in ctx.load_erratum_jobs('event-'): # read Jira issue configuration config = ErratumConfig.from_yaml_file(Path('component-config.yaml.sample')) @@ -142,6 +164,7 @@ def cmd_jira(ctx: CLIContext) -> None: print(f'* Would create a {action.type.name} issue:') print(f' summary: {action.summary}') print(f' summary: {action.description}') + print() if action.id in known_issues: raise Exception(f'Issue "{action.id}" is already created!') @@ -153,7 +176,6 @@ def cmd_jira(ctx: CLIContext) -> None: issue_actions.append(action) continue - print() print(f' Issue would be assigned to {action.assignee}.') print(f' rendered: >>{render_template(action.assignee, ERRATUM=erratum_job)}<<') print(f' Will remember the issue as `{action.id}`.') @@ -163,11 +185,20 @@ def cmd_jira(ctx: CLIContext) -> None: known_issues[action.id] = True - # erratum_job.issue = ... - # what's recipe? doesn't it belong to "schedule"? - # recipe = new JobRecipe(url) + # create a fake Issue object for now + issue = Issue(id=f'NEWA-{next(jira_id_gen)}') + + if action.job_recipe: + print( + f'* Would kick automated job for issue {action.type.name}' + f'based on recipe from {action.job_recipe}:') + print() - ctx.save_erratum_job('jira-', erratum_job) + jira_job = JiraJob(event=erratum_job.event, + erratum=erratum_job.erratum, + jira=issue, + recipe=Recipe(url=action.job_recipe)) + ctx.save_jira_job('jira-', jira_job) @main.command(name='schedule')