Skip to content

Commit 2a6dee5

Browse files
committed
One-step slack format
1 parent 68cdad0 commit 2a6dee5

File tree

5 files changed

+77
-19
lines changed

5 files changed

+77
-19
lines changed

src/redis_release/bht/tree.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def initialize_tree_and_state(
142142
args.slack_channel_id,
143143
args.slack_thread_ts,
144144
args.slack_reply_broadcast,
145+
args.slack_format,
145146
)
146147
# Capture the non-None printer in the closure
147148
printer = slack_printer

src/redis_release/cli.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,11 @@ def slack_bot(
271271
"--authorized-user",
272272
help="User ID authorized to run releases (can be specified multiple times). If not specified, all users are authorized",
273273
),
274+
slack_format: str = typer.Option(
275+
"default",
276+
"--slack-format",
277+
help="Slack message format: 'default' shows all steps, 'one-step' shows only the last step",
278+
),
274279
) -> None:
275280
"""Run Slack bot that listens for status requests.
276281
@@ -285,20 +290,33 @@ def slack_bot(
285290
286291
Requires Socket Mode to be enabled in your Slack app configuration.
287292
"""
293+
from redis_release.models import SlackFormat
288294
from redis_release.slack_bot import run_bot
289295

290296
setup_logging()
291297
config_path = config_file or "config.yaml"
292298

299+
# Parse slack_format
300+
try:
301+
slack_format_enum = SlackFormat(slack_format)
302+
except ValueError:
303+
logger.error(
304+
f"Invalid slack format: {slack_format}. Must be 'default' or 'one-step'"
305+
)
306+
raise typer.BadParameter(
307+
f"Invalid slack format: {slack_format}. Must be 'default' or 'one-step'"
308+
)
309+
293310
logger.info("Starting Slack bot...")
294311
asyncio.run(
295312
run_bot(
296-
config_path,
297-
slack_bot_token,
298-
slack_app_token,
299-
reply_in_thread,
300-
broadcast_to_channel,
301-
authorized_users,
313+
config_path=config_path,
314+
slack_bot_token=slack_bot_token,
315+
slack_app_token=slack_app_token,
316+
reply_in_thread=reply_in_thread,
317+
broadcast_to_channel=broadcast_to_channel,
318+
authorized_users=authorized_users,
319+
slack_format=slack_format_enum,
302320
)
303321
)
304322

src/redis_release/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ class ReleaseType(str, Enum):
6060
INTERNAL = "internal"
6161

6262

63+
class SlackFormat(str, Enum):
64+
"""Slack message format enumeration."""
65+
66+
DEFAULT = "default"
67+
ONE_STEP = "one-step"
68+
69+
6370
class WorkflowStatus(str, Enum):
6471
"""Workflow status enumeration."""
6572

@@ -241,3 +248,4 @@ class ReleaseArgs(BaseModel):
241248
slack_channel_id: Optional[str] = None
242249
slack_thread_ts: Optional[str] = None
243250
slack_reply_broadcast: bool = False
251+
slack_format: SlackFormat = SlackFormat.DEFAULT

src/redis_release/slack_bot.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from redis_release.bht.tree import async_tick_tock, initialize_tree_and_state
1515
from redis_release.config import Config, load_config
16-
from redis_release.models import ReleaseArgs
16+
from redis_release.models import ReleaseArgs, SlackFormat
1717
from redis_release.state_manager import S3StateStorage, StateManager
1818
from redis_release.state_slack import init_slack_printer
1919

@@ -34,6 +34,7 @@ def __init__(
3434
reply_in_thread: bool = True,
3535
broadcast_to_channel: bool = False,
3636
authorized_users: Optional[List[str]] = None,
37+
slack_format: SlackFormat = SlackFormat.DEFAULT,
3738
):
3839
"""Initialize the bot.
3940
@@ -44,11 +45,13 @@ def __init__(
4445
reply_in_thread: If True, reply in thread. If False, reply in main channel
4546
broadcast_to_channel: If True and reply_in_thread is True, also show in main channel
4647
authorized_users: List of user IDs authorized to run releases. If None, all users are authorized
48+
slack_format: Slack message format (default or one-step)
4749
"""
4850
self.config = config
4951
self.reply_in_thread = reply_in_thread
5052
self.broadcast_to_channel = broadcast_to_channel
5153
self.authorized_users = authorized_users or []
54+
self.slack_format = slack_format
5255

5356
# Get tokens from args or environment
5457
bot_token = slack_bot_token or os.environ.get("SLACK_BOT_TOKEN")
@@ -261,6 +264,7 @@ def run_release_in_thread() -> None:
261264
slack_channel_id=channel,
262265
slack_thread_ts=thread_ts if self.reply_in_thread else None,
263266
slack_reply_broadcast=reply_broadcast,
267+
slack_format=self.slack_format,
264268
)
265269

266270
# Run the release
@@ -355,6 +359,7 @@ async def _post_status(
355359
slack_channel_id=channel,
356360
thread_ts=thread_ts if self.reply_in_thread else None,
357361
reply_broadcast=self.broadcast_to_channel,
362+
slack_format=self.slack_format,
358363
)
359364
printer.update_message(state)
360365

@@ -391,6 +396,7 @@ async def run_bot(
391396
reply_in_thread: bool = True,
392397
broadcast_to_channel: bool = False,
393398
authorized_users: Optional[List[str]] = None,
399+
slack_format: SlackFormat = SlackFormat.DEFAULT,
394400
) -> None:
395401
"""Run the Slack bot.
396402
@@ -401,6 +407,7 @@ async def run_bot(
401407
reply_in_thread: If True, reply in thread. If False, reply in main channel
402408
broadcast_to_channel: If True and reply_in_thread is True, also show in main channel
403409
authorized_users: List of user IDs authorized to run releases. If None, all users are authorized
410+
slack_format: Slack message format (default or one-step)
404411
"""
405412
# Load config
406413
config = load_config(config_path)
@@ -413,6 +420,7 @@ async def run_bot(
413420
reply_in_thread=reply_in_thread,
414421
broadcast_to_channel=broadcast_to_channel,
415422
authorized_users=authorized_users,
423+
slack_format=slack_format,
416424
)
417425

418426
await bot.start()

src/redis_release/state_slack.py

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from slack_sdk import WebClient
1010
from slack_sdk.errors import SlackApiError
1111

12+
from redis_release.models import SlackFormat
1213
from redis_release.state_display import DisplayModel, StepStatus
1314

1415
from .bht.state import (
@@ -44,6 +45,7 @@ def init_slack_printer(
4445
slack_channel_id: Optional[str],
4546
thread_ts: Optional[str] = None,
4647
reply_broadcast: bool = False,
48+
slack_format: SlackFormat = SlackFormat.DEFAULT,
4749
) -> "SlackStatePrinter":
4850
"""Initialize SlackStatePrinter with validation.
4951
@@ -52,6 +54,7 @@ def init_slack_printer(
5254
slack_channel_id: Slack channel ID to post to
5355
thread_ts: Optional thread timestamp to post messages in a thread
5456
reply_broadcast: If True and thread_ts is set, also show in main channel
57+
slack_format: Slack message format (default or one-step)
5558
5659
Returns:
5760
SlackStatePrinter instance
@@ -69,7 +72,9 @@ def init_slack_printer(
6972
"Slack token not provided. Use slack_token argument or set SLACK_BOT_TOKEN environment variable"
7073
)
7174

72-
return SlackStatePrinter(token, slack_channel_id, thread_ts, reply_broadcast)
75+
return SlackStatePrinter(
76+
token, slack_channel_id, thread_ts, reply_broadcast, slack_format
77+
)
7378

7479

7580
class SlackStatePrinter:
@@ -81,6 +86,7 @@ def __init__(
8186
slack_channel_id: str,
8287
thread_ts: Optional[str] = None,
8388
reply_broadcast: bool = False,
89+
slack_format: SlackFormat = SlackFormat.DEFAULT,
8490
):
8591
"""Initialize the Slack printer.
8692
@@ -89,11 +95,13 @@ def __init__(
8995
slack_channel_id: Slack channel ID to post messages to
9096
thread_ts: Optional thread timestamp to post messages in a thread
9197
reply_broadcast: If True and thread_ts is set, also show in main channel
98+
slack_format: Slack message format (default or one-step)
9299
"""
93100
self.client = WebClient(token=slack_token)
94101
self.channel_id: str = slack_channel_id
95102
self.thread_ts = thread_ts
96103
self.reply_broadcast = reply_broadcast
104+
self.slack_format = slack_format
97105
self.message_ts: Optional[str] = None
98106
self.last_blocks_json: Optional[str] = None
99107
self.started_at = datetime.now(timezone.utc)
@@ -409,16 +417,31 @@ def _format_steps_for_slack(
409417
details: List[str] = []
410418
details.append(f"*{prefix}*")
411419

412-
for step_status, step_name, step_message in steps:
413-
if step_status == StepStatus.SUCCEEDED:
414-
details.append(f"• ✅ {step_name}")
415-
elif step_status == StepStatus.RUNNING:
416-
details.append(f"• ⏳ {step_name}")
417-
elif step_status == StepStatus.NOT_STARTED:
418-
details.append(f"• ⚪ {step_name}")
419-
else: # FAILED or INCORRECT
420-
msg = f" ({step_message})" if step_message else ""
421-
details.append(f"• ❌ {step_name}{msg}")
422-
break
420+
# If one-step format, only show the last step
421+
if self.slack_format == SlackFormat.ONE_STEP:
422+
if steps:
423+
step_status, step_name, step_message = steps[-1]
424+
if step_status == StepStatus.SUCCEEDED:
425+
details.append(f"• ✅ {step_name}")
426+
elif step_status == StepStatus.RUNNING:
427+
details.append(f"• ⏳ {step_name}")
428+
elif step_status == StepStatus.NOT_STARTED:
429+
details.append(f"• ⚪ {step_name}")
430+
else: # FAILED or INCORRECT
431+
msg = f" ({step_message})" if step_message else ""
432+
details.append(f"• ❌ {step_name}{msg}")
433+
else:
434+
# Default format: show all steps
435+
for step_status, step_name, step_message in steps:
436+
if step_status == StepStatus.SUCCEEDED:
437+
details.append(f"• ✅ {step_name}")
438+
elif step_status == StepStatus.RUNNING:
439+
details.append(f"• ⏳ {step_name}")
440+
elif step_status == StepStatus.NOT_STARTED:
441+
details.append(f"• ⚪ {step_name}")
442+
else: # FAILED or INCORRECT
443+
msg = f" ({step_message})" if step_message else ""
444+
details.append(f"• ❌ {step_name}{msg}")
445+
break
423446

424447
return details

0 commit comments

Comments
 (0)