Skip to content

Commit 0fa7dfb

Browse files
pditommasoclaude
andauthored
Add Platform workflowId to SeqeraExecutor session labels (#6818) [ci fast]
Write the Platform-assigned workflowId into WorkflowMetadata via a new PlatformMetadata class so the Seqera scheduler can correlate jobs back to Platform runs. - Add PlatformMetadata with lazy-init getter on WorkflowMetadata - TowerClient.onFlowCreate() writes workflowId to platform metadata - Labels emits seqera.io/platform/workflowId label Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 96d06fe commit 0fa7dfb

File tree

7 files changed

+161
-4
lines changed

7 files changed

+161
-4
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2013-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+
18+
package nextflow.script
19+
20+
import groovy.transform.CompileStatic
21+
import groovy.transform.EqualsAndHashCode
22+
import groovy.transform.ToString
23+
24+
/**
25+
* Models Seqera Platform metadata for Nextflow execution
26+
*
27+
* @author Paolo Di Tommaso <paolo.ditommaso@gmail.com>
28+
*/
29+
@CompileStatic
30+
@ToString(includeNames = true, includePackage = false)
31+
@EqualsAndHashCode
32+
class PlatformMetadata {
33+
34+
String workflowId
35+
36+
PlatformMetadata() {}
37+
38+
PlatformMetadata(String workflowId) {
39+
this.workflowId = workflowId
40+
}
41+
}

modules/nextflow/src/main/groovy/nextflow/script/WorkflowMetadata.groovy

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import nextflow.exception.WorkflowScriptErrorException
3434
import nextflow.trace.WorkflowStats
3535
import nextflow.util.Duration
3636
import nextflow.util.TestOnly
37-
import org.codehaus.groovy.runtime.InvokerHelper
3837
/**
3938
* Models workflow metadata properties and notification handler
4039
*
@@ -218,6 +217,12 @@ class WorkflowMetadata {
218217
*/
219218
FusionMetadata fusion
220219

220+
/**
221+
* Metadata specific to Seqera Platform, including:
222+
* <li>workflowId: the Platform-assigned workflow identifier
223+
*/
224+
PlatformMetadata platform
225+
221226
/**
222227
* The list of files that concurred to create the config object
223228
*/
@@ -497,4 +502,14 @@ class WorkflowMetadata {
497502
session.statsObserver.getStats()
498503
}
499504

505+
PlatformMetadata getPlatform() {
506+
if( platform!=null )
507+
return platform
508+
synchronized (this) {
509+
if( platform!=null )
510+
return platform
511+
platform = new PlatformMetadata()
512+
}
513+
return platform
514+
}
500515
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2013-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+
18+
package nextflow.script
19+
20+
import spock.lang.Specification
21+
22+
/**
23+
* Tests for {@link PlatformMetadata}
24+
*
25+
* @author Paolo Di Tommaso <paolo.ditommaso@gmail.com>
26+
*/
27+
class PlatformMetadataTest extends Specification {
28+
29+
def 'should create with default constructor'() {
30+
when:
31+
def meta = new PlatformMetadata()
32+
33+
then:
34+
meta.workflowId == null
35+
}
36+
37+
def 'should create with workflowId'() {
38+
when:
39+
def meta = new PlatformMetadata('abc123')
40+
41+
then:
42+
meta.workflowId == 'abc123'
43+
}
44+
45+
def 'should allow setting workflowId after construction'() {
46+
given:
47+
def meta = new PlatformMetadata()
48+
49+
when:
50+
meta.workflowId = 'xyz789'
51+
52+
then:
53+
meta.workflowId == 'xyz789'
54+
}
55+
}

plugins/nf-seqera/src/main/io/seqera/executor/Labels.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ class Labels {
4949
if( workflow.sessionId )
5050
entries.put('nextflow.io/sessionId', workflow.sessionId.toString())
5151
entries.put('nextflow.io/resume', String.valueOf(workflow.resume))
52-
if( workflow.sessionId && workflow.runName )
53-
entries.put('seqera.io/runId', runId(workflow.sessionId.toString(), workflow.runName))
5452
if( workflow.revision )
5553
entries.put('nextflow.io/revision', workflow.revision)
5654
if( workflow.commitId )
@@ -61,6 +59,8 @@ class Labels {
6159
entries.put('nextflow.io/manifestName', workflow.manifest.name)
6260
if( NextflowMeta.instance.version )
6361
entries.put('nextflow.io/runtimeVersion', NextflowMeta.instance.version.toString())
62+
if( workflow.platform?.workflowId )
63+
entries.put('seqera.io/platform/workflowId', workflow.platform.workflowId)
6464
return this
6565
}
6666

plugins/nf-seqera/src/test/io/seqera/executor/LabelsTest.groovy

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package io.seqera.executor
1919

2020
import nextflow.NextflowMeta
2121
import nextflow.config.Manifest
22+
import nextflow.script.PlatformMetadata
2223
import nextflow.script.WorkflowMetadata
2324
import spock.lang.Specification
2425

@@ -59,7 +60,6 @@ class LabelsTest extends Specification {
5960
labels.entries['nextflow.io/repository'] == 'https://github.com/nf-core/rnaseq'
6061
labels.entries['nextflow.io/manifestName'] == 'nf-core/rnaseq'
6162
labels.entries['nextflow.io/runtimeVersion'] == NextflowMeta.instance.version.toString()
62-
labels.entries['seqera.io/runId'] == Labels.runId(sessionId.toString(), 'crazy_darwin')
6363
}
6464

6565
def 'should compute stable runId from sessionId and runName'() {
@@ -150,6 +150,46 @@ class LabelsTest extends Specification {
150150
labels.entries['nextflow.io/projectName'] == 'hello'
151151
}
152152

153+
def 'should include platform workflowId when available'() {
154+
given:
155+
def workflow = Mock(WorkflowMetadata) {
156+
getProjectName() >> 'hello'
157+
getUserName() >> 'user1'
158+
getRunName() >> 'happy_turing'
159+
getSessionId() >> UUID.randomUUID()
160+
isResume() >> false
161+
getManifest() >> new Manifest([:])
162+
getPlatform() >> new PlatformMetadata('wf-abc123')
163+
}
164+
165+
when:
166+
def labels = new Labels()
167+
.withWorkflowMetadata(workflow)
168+
169+
then:
170+
labels.entries['seqera.io/platform/workflowId'] == 'wf-abc123'
171+
}
172+
173+
def 'should omit platform workflowId when not set'() {
174+
given:
175+
def workflow = Mock(WorkflowMetadata) {
176+
getProjectName() >> 'hello'
177+
getUserName() >> 'user1'
178+
getRunName() >> 'happy_turing'
179+
getSessionId() >> UUID.randomUUID()
180+
isResume() >> false
181+
getManifest() >> new Manifest([:])
182+
getPlatform() >> new PlatformMetadata()
183+
}
184+
185+
when:
186+
def labels = new Labels()
187+
.withWorkflowMetadata(workflow)
188+
189+
then:
190+
!labels.entries.containsKey('seqera.io/platform/workflowId')
191+
}
192+
153193
def 'should handle null user labels'() {
154194
when:
155195
def labels = new Labels()

plugins/nf-tower/src/main/io/seqera/tower/plugin/TowerClient.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ class TowerClient implements TraceObserverV2 {
292292
this.workflowId = ret.workflowId
293293
if( !workflowId )
294294
throw new AbortOperationException("Invalid Seqera Platform API response - Missing workflow Id")
295+
session.workflowMetadata.platform.workflowId = workflowId
295296
if( ret.message )
296297
log.warn(ret.message.toString())
297298

plugins/nf-tower/src/test/io/seqera/tower/plugin/TowerClientTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import nextflow.cloud.types.PriceModel
3030
import nextflow.container.DockerConfig
3131
import nextflow.container.resolver.ContainerMeta
3232
import nextflow.exception.AbortOperationException
33+
import nextflow.script.PlatformMetadata
3334
import nextflow.script.ScriptBinding
3435
import nextflow.script.WorkflowMetadata
3536
import nextflow.trace.TraceRecord
@@ -378,9 +379,11 @@ class TowerClientTest extends Specification {
378379
def 'should post create request' () {
379380
given:
380381
def uuid = UUID.randomUUID()
382+
def platform = new PlatformMetadata()
381383
def meta = Mock(WorkflowMetadata) {
382384
getProjectName() >> 'the-project-name'
383385
getRepository() >> 'git://repo.com/foo'
386+
getPlatform() >> platform
384387
}
385388
def session = Mock(Session) {
386389
getUniqueId() >> uuid
@@ -403,6 +406,8 @@ class TowerClientTest extends Specification {
403406
and:
404407
client.workflowId == 'xyz123'
405408
!client.towerLaunch
409+
and:
410+
platform.workflowId == 'xyz123'
406411

407412
}
408413

0 commit comments

Comments
 (0)