|
3 | 3 | import json |
4 | 4 | import logging |
5 | 5 | import os |
| 6 | +from datetime import datetime, timezone |
6 | 7 | from typing import Any, Dict, List, Optional, Tuple |
7 | 8 |
|
8 | 9 | from slack_sdk import WebClient |
|
15 | 16 | logger = logging.getLogger(__name__) |
16 | 17 |
|
17 | 18 |
|
| 19 | +def get_workflow_link(repo: str, run_id: Optional[int]) -> Optional[str]: |
| 20 | + """Generate GitHub workflow URL from repo and run_id. |
| 21 | +
|
| 22 | + Args: |
| 23 | + repo: Repository in format "owner/repo" |
| 24 | + run_id: GitHub workflow run ID |
| 25 | +
|
| 26 | + Returns: |
| 27 | + GitHub workflow URL or None if run_id is not available |
| 28 | + """ |
| 29 | + if not run_id or not repo: |
| 30 | + return None |
| 31 | + return f"https://github.com/{repo}/actions/runs/{run_id}" |
| 32 | + |
| 33 | + |
18 | 34 | def init_slack_printer( |
19 | 35 | slack_token: Optional[str], slack_channel_id: Optional[str] |
20 | 36 | ) -> "SlackStatePrinter": |
@@ -57,6 +73,7 @@ def __init__(self, slack_token: str, slack_channel_id: str): |
57 | 73 | self.channel_id = slack_channel_id |
58 | 74 | self.message_ts: Optional[str] = None |
59 | 75 | self.last_blocks_json: Optional[str] = None |
| 76 | + self.started_at = datetime.now(timezone.utc) |
60 | 77 |
|
61 | 78 | def update_message(self, state: ReleaseState) -> bool: |
62 | 79 | """Post or update Slack message with release state. |
@@ -131,19 +148,37 @@ def _make_blocks(self, state: ReleaseState) -> List[Dict[str, Any]]: |
131 | 148 | } |
132 | 149 | ) |
133 | 150 |
|
134 | | - # Legend |
| 151 | + # Show started date (when SlackStatePrinter was created) |
| 152 | + started_str = self.started_at.strftime("%Y-%m-%d %H:%M:%S %Z") |
135 | 153 | blocks.append( |
136 | 154 | { |
137 | 155 | "type": "context", |
138 | 156 | "elements": [ |
139 | 157 | { |
140 | 158 | "type": "mrkdwn", |
141 | | - "text": "*Legend:* ✅ Success • ❌ Failed • ⏳ In progress • ⚪ Not started", |
| 159 | + "text": f"*Started:* {started_str}", |
142 | 160 | } |
143 | 161 | ], |
144 | 162 | } |
145 | 163 | ) |
146 | 164 |
|
| 165 | + # Legend with two columns |
| 166 | + blocks.append( |
| 167 | + { |
| 168 | + "type": "context", |
| 169 | + "elements": [ |
| 170 | + { |
| 171 | + "type": "mrkdwn", |
| 172 | + "text": "✅ Success\n❌ Failed", |
| 173 | + }, |
| 174 | + { |
| 175 | + "type": "mrkdwn", |
| 176 | + "text": "⏳ In progress\n⚪ Not started", |
| 177 | + }, |
| 178 | + ], |
| 179 | + } |
| 180 | + ) |
| 181 | + |
147 | 182 | blocks.append({"type": "divider"}) |
148 | 183 |
|
149 | 184 | # Process each package |
@@ -172,14 +207,32 @@ def _make_blocks(self, state: ReleaseState) -> List[Dict[str, Any]]: |
172 | 207 | if build_details or publish_details: |
173 | 208 | elements = [] |
174 | 209 | if build_details: |
| 210 | + # Create link for Build Workflow if run_id exists |
| 211 | + build_link = get_workflow_link( |
| 212 | + package.meta.repo, package.build.run_id |
| 213 | + ) |
| 214 | + build_title = ( |
| 215 | + f"<{build_link}|*Build Workflow*>" |
| 216 | + if build_link |
| 217 | + else "*Build Workflow*" |
| 218 | + ) |
175 | 219 | elements.append( |
176 | | - {"type": "mrkdwn", "text": f"*Build Workflow*\n{build_details}"} |
| 220 | + {"type": "mrkdwn", "text": f"{build_title}\n{build_details}"} |
177 | 221 | ) |
178 | 222 | if publish_details: |
| 223 | + # Create link for Publish Workflow if run_id exists |
| 224 | + publish_link = get_workflow_link( |
| 225 | + package.meta.repo, package.publish.run_id |
| 226 | + ) |
| 227 | + publish_title = ( |
| 228 | + f"<{publish_link}|*Publish Workflow*>" |
| 229 | + if publish_link |
| 230 | + else "*Publish Workflow*" |
| 231 | + ) |
179 | 232 | elements.append( |
180 | 233 | { |
181 | 234 | "type": "mrkdwn", |
182 | | - "text": f"*Publish Workflow*\n{publish_details}", |
| 235 | + "text": f"{publish_title}\n{publish_details}", |
183 | 236 | } |
184 | 237 | ) |
185 | 238 | blocks.append({"type": "context", "elements": elements}) |
|
0 commit comments