Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1431,6 +1431,28 @@ Example:
$ newa event --compose CentOS-Stream-9 job-recipe path/to/recipe.yaml schedule --no-reportportal execute
```

#### Option `--rp-launch-uuid`

Allows you to reuse an existing ReportPortal launch instead of creating a new one during test execution. When this option is provided, NEWA fetches the launch metadata (name, description, URL) from ReportPortal and configures all scheduled jobs to use this existing launch.

This option is mutually exclusive with `--no-reportportal`.

The typical use case is when you want to add test results to an existing ReportPortal launch, for example:
- Running additional tests for the same erratum or compose
- Re-running tests with different configurations but keeping results in the same launch
- Consolidating results from multiple NEWA runs into a single launch

**Important notes:**
- The launch UUID must exist in the configured ReportPortal project
- NEWA will validate the launch exists before scheduling
- All generated schedule jobs will use this launch UUID
- The `execute` command will skip creating a new launch and use the provided one

Example:
```
$ newa event --compose CentOS-Stream-9 jira --job-recipe path/to/recipe.yaml schedule --rp-launch-uuid 12345678-1234-1234-1234-123456789abc execute report
```


### Subcommand `cancel`

Expand Down
16 changes: 14 additions & 2 deletions newa/cli/commands/schedule_cmd.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Schedule command for NEWA CLI."""

import sys
from typing import Optional

import click

Expand Down Expand Up @@ -31,12 +32,17 @@
default=False,
help='Do not report test results to ReportPortal.',
)
@click.option(
'--rp-launch-uuid',
help='Reuse an existing ReportPortal launch UUID instead of creating a new one.',
)
@click.pass_obj
def cmd_schedule(
ctx: CLIContext,
arch: list[str],
fixtures: list[str],
no_reportportal: bool) -> None:
no_reportportal: bool,
rp_launch_uuid: Optional[str] = None) -> None:
"""
Schedule subcommand - creates schedule jobs from jira jobs.

Expand All @@ -48,6 +54,12 @@ def cmd_schedule(
"""
ctx.enter_command('schedule')

# Validate mutually exclusive options
if no_reportportal and rp_launch_uuid:
ctx.logger.error(
'ERROR: --no-reportportal and --rp-launch-uuid are mutually exclusive options')
sys.exit(1)

# Ensure state dir is present and initialized
initialize_state_dir(ctx)

Expand All @@ -68,4 +80,4 @@ def cmd_schedule(

# Process each jira job
for jira_job in jira_jobs:
_process_jira_job(ctx, jira_job, arch, fixtures, no_reportportal)
_process_jira_job(ctx, jira_job, arch, fixtures, no_reportportal, rp_launch_uuid)
49 changes: 47 additions & 2 deletions newa/cli/schedule_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os
import re
import sys
from typing import Any, Optional

from newa import (
Expand Down Expand Up @@ -65,7 +66,7 @@ def _process_fixtures(
return

for fixture in fixtures:
r = re.fullmatch(r'([^\s=]+)=([^=]*)', fixture)
r = re.fullmatch(r'([^\s=]+)=(.*)', fixture)
if not r:
raise Exception(
f"Fixture {fixture} does not having expected format 'name=value'")
Expand Down Expand Up @@ -170,7 +171,8 @@ def _process_jira_job(
jira_job: JiraJob,
arch_options: list[str],
fixtures: list[str],
no_reportportal: bool) -> None:
no_reportportal: bool,
rp_launch_uuid: Optional[str] = None) -> None:
"""Process a single jira_job and create schedule jobs."""
from newa import ScheduleJob

Expand All @@ -179,6 +181,35 @@ def _process_jira_job(
ctx.logger.info(f'Skipping jira job {jira_job.jira.id} - no recipe specified')
return

# Initialize ReportPortal connection if --rp-launch-uuid is provided
rp = None
launch_metadata: Optional[RawRecipeReportPortalConfigDimension] = None
if rp_launch_uuid:
from newa.cli.initialization import initialize_rp_connection
rp = initialize_rp_connection(ctx)

# Fetch launch metadata once (outside the request loop)
ctx.logger.info(f'Fetching ReportPortal launch {rp_launch_uuid}')
launch_info = rp.get_launch_info(rp_launch_uuid)

if not launch_info:
ctx.logger.error(
f'ERROR: Could not find ReportPortal launch {rp_launch_uuid} '
f'in project {rp.project}')
sys.exit(1)

# Store metadata to apply to all requests
launch_metadata = RawRecipeReportPortalConfigDimension(
launch_uuid=rp_launch_uuid,
launch_url=rp.get_launch_url(rp_launch_uuid),
launch_name=launch_info['name'],
launch_description=launch_info.get('description', ''),
)

ctx.logger.info(
f'Configured to reuse existing ReportPortal launch: '
f'{launch_info["name"]} ({rp_launch_uuid})')

# Determine compose and architectures
compose = jira_job.compose.id if jira_job.compose else None
architectures = _determine_architectures(ctx, arch_options, jira_job, compose)
Expand Down Expand Up @@ -218,6 +249,20 @@ def _process_jira_job(
jinja_vars = _prepare_jinja_vars_for_request(jira_job, request, issue_fields)
_render_request_attributes(request, jinja_vars)

# Apply cached launch metadata if --rp-launch-uuid was provided
if launch_metadata:
if not request.reportportal:
request.reportportal = RawRecipeReportPortalConfigDimension()
# Update individual fields to satisfy mypy TypedDict requirements
if 'launch_uuid' in launch_metadata:
request.reportportal['launch_uuid'] = launch_metadata['launch_uuid']
if 'launch_url' in launch_metadata:
request.reportportal['launch_url'] = launch_metadata['launch_url']
if 'launch_name' in launch_metadata:
request.reportportal['launch_name'] = launch_metadata['launch_name']
if 'launch_description' in launch_metadata:
request.reportportal['launch_description'] = launch_metadata['launch_description']

# Create and save schedule job
schedule_job = ScheduleJob(
event=jira_job.event,
Expand Down
Loading