Skip to content

Commit 528dc5f

Browse files
feat: add contextual Seqera Platform action buttons
Add Cancel, Resume, and Relaunch Slack buttons alongside View in Seqera Platform with link and interactive modes, configuration, tests, and docs. Partially addresses #65 Generated by Codex Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 54f0bd2 commit 528dc5f

12 files changed

Lines changed: 516 additions & 56 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- **Seqera Platform action buttons**: Contextual Cancel, Resume, and Relaunch buttons alongside View in Seqera Platform. Link mode (default) opens the Platform run page; interactive mode emits `action_id` values for external Slack interactivity handlers ([#65](https://github.com/seqeralabs/nf-slack/issues/65))
13+
1014
## [0.5.1] - 2026-02-19
1115

1216
### Fixed

docs/reference/api.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,21 @@ Configuration for workflow error notifications.
175175

176176
### `slack.seqeraPlatform`
177177

178-
| Property | Type | Default | Description |
179-
| --------- | --------- | ------- | --------------------------------------------------------- |
180-
| `enabled` | `Boolean` | `true` | Enable Seqera Platform deep link buttons in notifications |
178+
| Property | Type | Default | Description |
179+
| --------------- | --------- | ------- | ------------------------------------------------------- |
180+
| `enabled` | `Boolean` | `true` | Enable Seqera Platform action buttons in notifications |
181+
| `actionButtons` | `Map` | `{}` | Cancel/Resume/Relaunch button configuration (see below) |
182+
183+
#### `slack.seqeraPlatform.actionButtons`
184+
185+
| Property | Type | Default | Description |
186+
| ---------- | --------- | -------- | ----------------------------------------------------------------- |
187+
| `mode` | `String` | `'link'` | `'link'` for URL buttons, `'interactive'` for `action_id` buttons |
188+
| `cancel` | `Boolean` | `true` | Show Cancel on running/start/progress messages |
189+
| `resume` | `Boolean` | `true` | Show Resume on failure messages |
190+
| `relaunch` | `Boolean` | `true` | Show Relaunch on failure and completion messages |
191+
192+
Interactive mode requires an external Slack interactivity endpoint that verifies the signing secret and calls Seqera Platform APIs. The plugin only emits button payloads.
181193

182194
---
183195

docs/usage/guide.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ File paths are relative to the pipeline launch directory.
241241

242242
## Seqera Platform Integration
243243

244-
If you run pipelines through [Seqera Platform](https://seqera.io/platform/), nf-slack can automatically add deep links to the Platform run page:
244+
If you run pipelines through [Seqera Platform](https://seqera.io/platform/), nf-slack can automatically add contextual action buttons to Slack notifications:
245245

246246
```groovy
247247
slack {
@@ -251,10 +251,30 @@ slack {
251251
}
252252
seqeraPlatform {
253253
enabled = true
254+
actionButtons {
255+
mode = 'link' // default: URL buttons open the Platform run page
256+
cancel = true // show Cancel while running
257+
resume = true // show Resume on failure
258+
relaunch = true // show Relaunch on failure/completion
259+
}
254260
}
255261
}
256262
```
257263

264+
### Button behavior by workflow phase
265+
266+
| Phase | Buttons |
267+
| ------------------------ | ---------------------- |
268+
| Running (start/progress) | View, Cancel |
269+
| Failed (error) | View, Resume, Relaunch |
270+
| Completed | View, Relaunch |
271+
272+
In **link mode** (default), Cancel/Resume/Relaunch open the same Platform run page where those actions are available in the UI. One-click API execution from Slack requires an external interactivity handler.
273+
274+
### Interactive mode (advanced)
275+
276+
Set `actionButtons.mode = 'interactive'` to emit Slack buttons with `action_id` values (`seqera_platform_view`, `seqera_platform_cancel`, `seqera_platform_resume`, `seqera_platform_relaunch`) and the workflow run ID in `value`. A separate HTTP endpoint (with Slack signing secret verification) must call Seqera Platform APIs — this cannot run inside the Nextflow plugin during `nextflow run`.
277+
258278
## Custom Messages from Code
259279

260280
Send messages from anywhere in your Nextflow pipeline code using the `slackMessage()` and `slackFileUpload()` functions.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2025, Seqera Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package nextflow.slack
18+
19+
import groovy.transform.CompileStatic
20+
21+
/**
22+
* Workflow phase used to select contextual Seqera Platform action buttons.
23+
*/
24+
@CompileStatic
25+
enum SeqeraMessagePhase {
26+
RUNNING,
27+
COMPLETED,
28+
FAILED
29+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2025, Seqera Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package nextflow.slack
18+
19+
import groovy.transform.CompileStatic
20+
21+
/**
22+
* Configuration for Seqera Platform action buttons in Slack messages.
23+
*
24+
* <p>Link mode (default) emits URL buttons that open the Platform run page.
25+
* Interactive mode emits action_id buttons for an external Slack interactivity handler.
26+
*/
27+
@CompileStatic
28+
class SeqeraPlatformActionButtonsConfig {
29+
30+
static final String MODE_LINK = 'link'
31+
static final String MODE_INTERACTIVE = 'interactive'
32+
33+
final String mode
34+
final boolean cancel
35+
final boolean resume
36+
final boolean relaunch
37+
38+
SeqeraPlatformActionButtonsConfig(Map config) {
39+
config = config ?: [:]
40+
this.mode = (config.mode as String ?: MODE_LINK).toLowerCase()
41+
this.cancel = config.cancel != null ? config.cancel as boolean : true
42+
this.resume = config.resume != null ? config.resume as boolean : true
43+
this.relaunch = config.relaunch != null ? config.relaunch as boolean : true
44+
}
45+
46+
boolean isLinkMode() {
47+
return mode == MODE_LINK
48+
}
49+
50+
boolean isInteractiveMode() {
51+
return mode == MODE_INTERACTIVE
52+
}
53+
}

src/main/groovy/nextflow/slack/SeqeraPlatformConfig.groovy

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ import groovy.transform.CompileStatic
2121
/**
2222
* Configuration for Seqera Platform deep link integration.
2323
*
24-
* When Seqera Platform (Tower) is configured, adds a "View in Seqera Platform"
25-
* button to Slack notifications that links directly to the workflow run.
24+
* When Seqera Platform (Tower) is configured, adds contextual action buttons
25+
* to Slack notifications that link to the workflow run (or emit interactive
26+
* action IDs for external handlers).
2627
*
2728
* @author Adam Talbot <adam.talbot@seqera.io>
2829
*/
@@ -34,7 +35,14 @@ class SeqeraPlatformConfig {
3435
*/
3536
final boolean enabled
3637

38+
/**
39+
* Action button configuration (View / Cancel / Resume / Relaunch)
40+
*/
41+
final SeqeraPlatformActionButtonsConfig actionButtons
42+
3743
SeqeraPlatformConfig(Map config) {
38-
this.enabled = config?.enabled != null ? config.enabled as boolean : true
44+
config = config ?: [:]
45+
this.enabled = config.enabled != null ? config.enabled as boolean : true
46+
this.actionButtons = new SeqeraPlatformActionButtonsConfig(config.actionButtons as Map)
3947
}
4048
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2025, Seqera Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package nextflow.slack
18+
19+
import groovy.transform.CompileStatic
20+
21+
/**
22+
* Helpers for parsing Seqera Platform watch URLs returned by TowerClient.
23+
*/
24+
@CompileStatic
25+
class SeqeraPlatformUrlBuilder {
26+
27+
/**
28+
* Extract the workflow run ID from a Platform watch URL.
29+
* Example: https://cloud.seqera.io/orgs/foo/workspaces/bar/watch/abc123 -> abc123
30+
*/
31+
static String extractWorkflowRunId(String watchUrl) {
32+
if (!watchUrl) return null
33+
def marker = '/watch/'
34+
def start = watchUrl.indexOf(marker)
35+
if (start < 0) return null
36+
def idPart = watchUrl.substring(start + marker.length())
37+
def end = idPart.findIndexOf { it == '?' || it == '/' }
38+
return end >= 0 ? idPart.substring(0, end) : idPart
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2025, Seqera Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package nextflow.slack
18+
19+
import groovy.transform.CompileStatic
20+
21+
/**
22+
* Parsed Seqera Platform watch URL and workflow run identifier.
23+
*/
24+
@CompileStatic
25+
class SeqeraWatchContext {
26+
27+
final String watchUrl
28+
final String workflowRunId
29+
30+
SeqeraWatchContext(String watchUrl, String workflowRunId) {
31+
this.watchUrl = watchUrl
32+
this.workflowRunId = workflowRunId
33+
}
34+
35+
static SeqeraWatchContext fromWatchUrl(String watchUrl) {
36+
if (!watchUrl) return null
37+
def workflowRunId = SeqeraPlatformUrlBuilder.extractWorkflowRunId(watchUrl)
38+
return new SeqeraWatchContext(watchUrl, workflowRunId)
39+
}
40+
}

0 commit comments

Comments
 (0)