Skip to content

Commit 0d9da6c

Browse files
committed
Add publish phase
Extract workflow executor into separate classes Update what is success definition Remove force_rebuild from the state
1 parent b5b47dd commit 0d9da6c

File tree

4 files changed

+136
-158
lines changed

4 files changed

+136
-158
lines changed

src/redis_release/cli.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,12 @@ def status(
111111
table = Table(title=f"Release Status: {tag}")
112112
table.add_column("Package", style="cyan")
113113
table.add_column("Build Status", style="magenta")
114-
table.add_column("Artifacts", style="blue")
114+
table.add_column("Publish Status", style="green")
115+
table.add_column("Build Artifacts", style="blue")
116+
table.add_column("Publish Artifacts", style="yellow")
115117

116118
for pkg_type, pkg_state in state.packages.items():
119+
# Build status
117120
if not pkg_state.build_completed:
118121
build_status = "[blue]In Progress[/blue]"
119122
elif pkg_state.build_workflow and pkg_state.build_workflow.conclusion:
@@ -126,12 +129,32 @@ def status(
126129
else:
127130
build_status = "[yellow]Cancelled[/yellow]"
128131

129-
if pkg_state.artifacts:
130-
artifacts = f"[green]{len(pkg_state.artifacts)} artifacts[/green]"
132+
# Publish status
133+
if not pkg_state.publish_completed:
134+
publish_status = "[blue]In Progress[/blue]" if pkg_state.publish_workflow else "[dim]Not Started[/dim]"
135+
elif pkg_state.publish_workflow and pkg_state.publish_workflow.conclusion:
136+
if pkg_state.publish_workflow.conclusion.value == "success":
137+
publish_status = "[green]Success[/green]"
138+
elif pkg_state.publish_workflow.conclusion.value == "failure":
139+
publish_status = "[red]Failed[/red]"
140+
else:
141+
publish_status = "[yellow]Cancelled[/yellow]"
142+
else:
143+
publish_status = "[yellow]Cancelled[/yellow]"
144+
145+
# Build artifacts
146+
if pkg_state.build_artifacts:
147+
build_artifacts = f"[green]{len(pkg_state.build_artifacts)}[/green]"
148+
else:
149+
build_artifacts = "[dim]None[/dim]"
150+
151+
# Publish artifacts
152+
if pkg_state.publish_artifacts:
153+
publish_artifacts = f"[green]{len(pkg_state.publish_artifacts)}[/green]"
131154
else:
132-
artifacts = "[dim]None[/dim]"
155+
publish_artifacts = "[dim]None[/dim]"
133156

134-
table.add_row(pkg_type.value, build_status, artifacts)
157+
table.add_row(pkg_type.value, build_status, publish_status, build_artifacts, publish_artifacts)
135158

136159
console.print(table)
137160

@@ -146,16 +169,6 @@ def status(
146169
f" Docker repo: [cyan]{state.docker_repo_commit[:8]}[/cyan]"
147170
)
148171

149-
if state.is_build_phase_complete():
150-
console.print("[green]Release is complete![/green]")
151-
elif state.has_build_failures():
152-
console.print(
153-
"[red]Release failed - build phase completed with errors[/red]"
154-
)
155-
elif state.is_build_phase_finished():
156-
console.print("[yellow]Release completed but not successful[/yellow]")
157-
else:
158-
console.print("[blue]Building Docker image...[/blue]")
159172

160173
except Exception as e:
161174
console.print(f"[red] Failed to get status: {e}[/red]")

src/redis_release/github_client.py

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -264,12 +264,21 @@ def get_workflow_artifacts(self, repo: str, run_id: int) -> Dict[str, Dict]:
264264
"size_in_bytes": 1048576,
265265
"digest": "sha256:mock-digest"
266266
},
267-
"mock-artifact": {
267+
"release_info": {
268268
"id": 67890,
269269
"archive_download_url": f"https://api.github.com/repos/{repo}/actions/artifacts/67890/zip",
270270
"created_at": "2023-01-01T00:00:00Z",
271271
"expires_at": "2023-01-31T00:00:00Z",
272272
"updated_at": "2023-01-01T00:00:00Z",
273+
"size_in_bytes": 2097152,
274+
"digest": "sha256:mock-digest-info"
275+
},
276+
"mock-artifact": {
277+
"id": 11111,
278+
"archive_download_url": f"https://api.github.com/repos/{repo}/actions/artifacts/11111/zip",
279+
"created_at": "2023-01-01T00:00:00Z",
280+
"expires_at": "2023-01-31T00:00:00Z",
281+
"updated_at": "2023-01-01T00:00:00Z",
273282
"size_in_bytes": 2048576,
274283
"digest": "sha256:mock-digest-2"
275284
}
@@ -322,31 +331,33 @@ def get_workflow_artifacts(self, repo: str, run_id: int) -> Dict[str, Dict]:
322331
console.print(f"[red]Failed to get artifacts: {e}[/red]")
323332
return {}
324333

325-
def extract_release_handle(self, repo: str, artifacts: Dict[str, Dict]) -> Optional[Dict[str, Any]]:
326-
"""Extract release_handle JSON from artifacts.
334+
def extract_result(self, repo: str, artifacts: Dict[str, Dict], artifact_name: str, json_file_name: str) -> Optional[Dict[str, Any]]:
335+
"""Extract JSON result from artifacts.
327336
328337
Args:
329338
repo: Repository name
330339
artifacts: Dictionary of artifacts from get_workflow_artifacts
340+
artifact_name: Name of the artifact to extract from
341+
json_file_name: Name of the JSON file within the artifact
331342
332343
Returns:
333-
Parsed JSON content from release_handle.json file, or None if not found
344+
Parsed JSON content from the specified file, or None if not found
334345
"""
335-
if "release_handle" not in artifacts:
336-
console.print("[yellow]No release_handle artifact found[/yellow]")
346+
if artifact_name not in artifacts:
347+
console.print(f"[yellow]No {artifact_name} artifact found[/yellow]")
337348
return None
338349

339-
release_handle_artifact = artifacts["release_handle"]
340-
artifact_id = release_handle_artifact.get("id")
350+
target_artifact = artifacts[artifact_name]
351+
artifact_id = target_artifact.get("id")
341352

342353
if not artifact_id:
343-
console.print("[red]release_handle artifact has no ID[/red]")
354+
console.print(f"[red]{artifact_name} artifact has no ID[/red]")
344355
return None
345356

346-
console.print(f"[blue]Extracting release_handle from artifact {artifact_id}[/blue]")
357+
console.print(f"[blue]Extracting {json_file_name} from artifact {artifact_id}[/blue]")
347358

348359
if self.dry_run:
349-
console.print("[yellow] (DRY RUN - returning mock release_handle)[/yellow]")
360+
console.print(f"[yellow] (DRY RUN - returning mock {json_file_name})[/yellow]")
350361
return {
351362
"mock": True,
352363
"version": "1.0.0",
@@ -356,10 +367,10 @@ def extract_release_handle(self, repo: str, artifacts: Dict[str, Dict]) -> Optio
356367
}
357368
}
358369

359-
# Download the artifact and extract release_handle.json
360-
download_url = release_handle_artifact.get("archive_download_url")
370+
# Download the artifact and extract JSON file
371+
download_url = target_artifact.get("archive_download_url")
361372
if not download_url:
362-
console.print("[red]release_handle artifact has no download URL[/red]")
373+
console.print(f"[red]{artifact_name} artifact has no download URL[/red]")
363374
return None
364375

365376
headers = {
@@ -373,27 +384,41 @@ def extract_release_handle(self, repo: str, artifacts: Dict[str, Dict]) -> Optio
373384
response = requests.get(download_url, headers=headers, timeout=30)
374385
response.raise_for_status()
375386

376-
# Extract release_handle.json from the zip
387+
# Extract JSON file from the zip
377388
import zipfile
378389
import io
379390

380391
with zipfile.ZipFile(io.BytesIO(response.content)) as zip_file:
381-
if "release_handle.json" in zip_file.namelist():
382-
with zip_file.open("release_handle.json") as json_file:
383-
release_handle_data = json.load(json_file)
384-
console.print("[green]Successfully extracted release_handle.json[/green]")
385-
return release_handle_data
392+
if json_file_name in zip_file.namelist():
393+
with zip_file.open(json_file_name) as json_file:
394+
result_data = json.load(json_file)
395+
console.print(f"[green]Successfully extracted {json_file_name}[/green]")
396+
return result_data
386397
else:
387-
console.print("[red]release_handle.json not found in artifact[/red]")
398+
console.print(f"[red]{json_file_name} not found in artifact[/red]")
388399
return None
389400

390401
except requests.exceptions.RequestException as e:
391-
console.print(f"[red]Failed to download release_handle artifact: {e}[/red]")
402+
console.print(f"[red]Failed to download {artifact_name} artifact: {e}[/red]")
392403
return None
393404
except (zipfile.BadZipFile, json.JSONDecodeError, KeyError) as e:
394-
console.print(f"[red]Failed to extract release_handle.json: {e}[/red]")
405+
console.print(f"[red]Failed to extract {json_file_name}: {e}[/red]")
395406
return None
396407

408+
def extract_release_handle(self, repo: str, artifacts: Dict[str, Dict]) -> Optional[Dict[str, Any]]:
409+
"""Extract release_handle JSON from artifacts.
410+
411+
This is a backward compatibility wrapper around extract_result.
412+
413+
Args:
414+
repo: Repository name
415+
artifacts: Dictionary of artifacts from get_workflow_artifacts
416+
417+
Returns:
418+
Parsed JSON content from release_handle.json file, or None if not found
419+
"""
420+
return self.extract_result(repo, artifacts, "release_handle", "release_handle.json")
421+
397422
def _get_recent_workflow_runs(
398423
self, repo: str, workflow_file: str, limit: int = 10
399424
) -> List[WorkflowRun]:

src/redis_release/models.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,31 @@ class PackageState(BaseModel):
5353

5454
package_type: PackageType
5555
build_workflow: Optional[WorkflowRun] = None
56-
artifacts: Dict[str, Dict[str, Any]] = Field(default_factory=dict)
56+
build_artifacts: Dict[str, Dict[str, Any]] = Field(default_factory=dict)
5757
release_handle: Optional[Dict[str, Any]] = None
5858
build_completed: bool = False
5959

6060
# Publish phase information
6161
publish_workflow: Optional[WorkflowRun] = None
6262
publish_completed: bool = False
6363
publish_info: Optional[Dict[str, Any]] = None
64+
publish_artifacts: Dict[str, Dict[str, Any]] = Field(default_factory=dict)
65+
66+
def is_build_phase_successful(self) -> bool:
67+
"""Check if build workflow is completed successfully."""
68+
return (
69+
self.build_completed
70+
and self.build_workflow is not None
71+
and self.build_workflow.conclusion == WorkflowConclusion.SUCCESS
72+
)
73+
74+
def is_publish_phase_successful(self) -> bool:
75+
"""Check if publish workflow is completed successfully."""
76+
return (
77+
self.publish_completed
78+
and self.publish_workflow is not None
79+
and self.publish_workflow.conclusion == WorkflowConclusion.SUCCESS
80+
)
6481

6582

6683
class ReleaseState(BaseModel):
@@ -76,14 +93,12 @@ class ReleaseState(BaseModel):
7693
redis_tag_commit: Optional[str] = None # Redis tag commit hash
7794
docker_repo_commit: Optional[str] = None # Docker repo latest commit hash
7895

79-
def is_build_phase_complete(self) -> bool:
96+
def is_build_successful(self) -> bool:
8097
"""Check if all build workflows are completed successfully."""
8198
if not self.packages:
8299
return False
83100
return all(
84-
pkg.build_completed
85-
and pkg.build_workflow
86-
and pkg.build_workflow.conclusion == WorkflowConclusion.SUCCESS
101+
pkg.is_build_phase_successful()
87102
for pkg in self.packages.values()
88103
)
89104

@@ -104,14 +119,12 @@ def has_build_failures(self) -> bool:
104119
for pkg in self.packages.values()
105120
)
106121

107-
def is_publish_phase_complete(self) -> bool:
122+
def is_publish_successful(self) -> bool:
108123
"""Check if all publish workflows are completed successfully."""
109124
if not self.packages:
110125
return False
111126
return all(
112-
pkg.publish_completed
113-
and pkg.publish_workflow
114-
and pkg.publish_workflow.conclusion == WorkflowConclusion.SUCCESS
127+
pkg.is_publish_phase_successful()
115128
for pkg in self.packages.values()
116129
)
117130

0 commit comments

Comments
 (0)