• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

common-workflow-language / cwlviewer / #1997

13 May 2026 05:20PM UTC coverage: 70.25% (-0.08%) from 70.334%
#1997

Pull #751

github

kinow
Undo code deletion, but replace string concatenation by log+parameters
Pull Request #751: Bump org.springframework.boot:spring-boot-starter-parent from 3.1.4 to 4.1.0-RC1

119 of 196 new or added lines in 32 files covered. (60.71%)

20 existing lines in 4 files now uncovered.

1712 of 2437 relevant lines covered (70.25%)

0.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.16
/src/main/java/org/commonwl/view/workflow/WorkflowService.java
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements.  See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership.  The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19

20
package org.commonwl.view.workflow;
21

22
import java.io.File;
23
import java.io.IOException;
24
import java.nio.file.Files;
25
import java.nio.file.Path;
26
import java.nio.file.Paths;
27
import java.util.ArrayList;
28
import java.util.Calendar;
29
import java.util.Date;
30
import java.util.List;
31
import java.util.Objects;
32
import java.util.Optional;
33
import java.util.UUID;
34
import org.commonwl.view.cwl.CWLService;
35
import org.commonwl.view.cwl.CWLToolRunner;
36
import org.commonwl.view.cwl.CWLToolStatus;
37
import org.commonwl.view.cwl.CWLValidationException;
38
import org.commonwl.view.git.GitDetails;
39
import org.commonwl.view.git.GitSemaphore;
40
import org.commonwl.view.git.GitService;
41
import org.commonwl.view.graphviz.GraphVizService;
42
import org.commonwl.view.researchobject.ROBundleFactory;
43
import org.commonwl.view.researchobject.ROBundleNotFoundException;
44
import org.commonwl.view.util.FileUtils;
45
import org.eclipse.jgit.api.Git;
46
import org.eclipse.jgit.api.errors.GitAPIException;
47
import org.eclipse.jgit.api.errors.RefNotFoundException;
48
import org.slf4j.Logger;
49
import org.slf4j.LoggerFactory;
50
import org.springframework.beans.factory.annotation.Autowired;
51
import org.springframework.beans.factory.annotation.Value;
52
import org.springframework.core.io.FileSystemResource;
53
import org.springframework.data.domain.Page;
54
import org.springframework.data.domain.Pageable;
55
import org.springframework.stereotype.Service;
56

57
@Service
58
public class WorkflowService {
59

60
  private final Logger logger = LoggerFactory.getLogger(this.getClass());
1✔
61

62
  private final GitService gitService;
63
  private final CWLService cwlService;
64
  private final WorkflowRepository workflowRepository;
65
  private final QueuedWorkflowRepository queuedWorkflowRepository;
66
  private final ROBundleFactory ROBundleFactory;
67
  private final GraphVizService graphVizService;
68
  private final CWLToolRunner cwlToolRunner;
69
  private final GitSemaphore gitSemaphore;
70
  private final int cacheDays;
71

72
  @Autowired
73
  public WorkflowService(
74
      GitService gitService,
75
      CWLService cwlService,
76
      WorkflowRepository workflowRepository,
77
      QueuedWorkflowRepository queuedWorkflowRepository,
78
      ROBundleFactory ROBundleFactory,
79
      GraphVizService graphVizService,
80
      CWLToolRunner cwlToolRunner,
81
      GitSemaphore gitSemaphore,
82
      @Value("${cacheDays}") int cacheDays) {
1✔
83
    this.gitService = gitService;
1✔
84
    this.cwlService = cwlService;
1✔
85
    this.workflowRepository = workflowRepository;
1✔
86
    this.queuedWorkflowRepository = queuedWorkflowRepository;
1✔
87
    this.ROBundleFactory = ROBundleFactory;
1✔
88
    this.graphVizService = graphVizService;
1✔
89
    this.cwlToolRunner = cwlToolRunner;
1✔
90
    this.cacheDays = cacheDays;
1✔
91
    this.gitSemaphore = gitSemaphore;
1✔
92
  }
1✔
93

94
  /**
95
   * Gets a page of all workflows from the database
96
   *
97
   * @param pageable The details of the page to be requested
98
   * @return The resulting page of the workflow entries
99
   */
100
  public Page<Workflow> getPageOfWorkflows(Pageable pageable) {
101
    return workflowRepository.findAllByOrderByRetrievedOnDesc(pageable);
×
102
  }
103

104
  /**
105
   * Gets a page of all workflows from the database
106
   *
107
   * @param searchString The string to search for
108
   * @param pageable The details of the page to be requested
109
   * @return The resulting page of the workflow entries
110
   */
111
  public Page<Workflow> searchPageOfWorkflows(String searchString, Pageable pageable) {
112
    return workflowRepository.findByLabelContainingOrDocContainingIgnoreCase(
×
113
        searchString, searchString, pageable);
114
  }
115

116
  /**
117
   * Get a workflow from the database by its ID
118
   *
119
   * @param id The ID of the workflow
120
   * @return The model for the workflow
121
   */
122
  public Workflow getWorkflow(String id) {
NEW
123
    return workflowRepository.findById(UUID.fromString(id)).orElse(null);
×
124
  }
125

126
  /**
127
   * Get a queued workflow from the database by its ID
128
   *
129
   * @param id The ID of the queued workflow
130
   * @return The model for the queued workflow
131
   */
132
  public QueuedWorkflow getQueuedWorkflow(String id) {
NEW
133
    return queuedWorkflowRepository.findById(UUID.fromString(id)).orElse(null);
×
134
  }
135

136
  /**
137
   * Get a queued workflow from the database
138
   *
139
   * @param githubInfo GitHub information for the workflow
140
   * @return The queued workflow model
141
   */
142
  public QueuedWorkflow getQueuedWorkflow(GitDetails githubInfo) {
143
    QueuedWorkflow queued = queuedWorkflowRepository.findByRetrievedFrom(githubInfo);
1✔
144

145
    // Slash in branch fix
146
    boolean slashesInPath = true;
1✔
147
    while (queued == null && slashesInPath) {
1✔
148
      GitDetails correctedForSlash = gitService.transferPathToBranch(githubInfo);
1✔
149
      if (correctedForSlash != null) {
1✔
150
        githubInfo = correctedForSlash;
×
151
        queued = queuedWorkflowRepository.findByRetrievedFrom(githubInfo);
×
152
      } else {
153
        slashesInPath = false;
1✔
154
      }
155
    }
1✔
156

157
    return queued;
1✔
158
  }
159

160
  /**
161
   * Get a workflow from the database, refreshing it if cache has expired
162
   *
163
   * @param gitInfo Git information for the workflow
164
   * @return The workflow model associated with gitInfo
165
   */
166
  public Workflow getWorkflow(GitDetails gitInfo) {
167
    // Check database for existing workflows from this repository
168
    Workflow workflow = workflowRepository.findByRetrievedFrom(gitInfo);
1✔
169

170
    // Slash in branch fix
171
    boolean slashesInPath = true;
1✔
172
    while (workflow == null && slashesInPath) {
1✔
173
      GitDetails correctedForSlash = gitService.transferPathToBranch(gitInfo);
×
174
      if (correctedForSlash != null) {
×
175
        gitInfo = correctedForSlash;
×
176
        workflow = workflowRepository.findByRetrievedFrom(gitInfo);
×
177
      } else {
178
        slashesInPath = false;
×
179
      }
180
    }
×
181

182
    // Cache update
183
    if (workflow != null) {
1✔
184
      // Delete the existing workflow if the cache has expired
185
      if (cacheExpired(workflow)) {
1✔
186
        removeWorkflow(workflow);
1✔
187

188
        // Add the new workflow if it exists
189
        try {
190
          createQueuedWorkflow(workflow.getRetrievedFrom());
1✔
191

192
          // Add the old commit for the purposes of permalinks
193
          // TODO: Separate concept of commit from branch ref, see #164
194
          GitDetails byOldCommitId = workflow.getRetrievedFrom();
1✔
195
          byOldCommitId.setBranch(workflow.getLastCommit());
1✔
196
          if (getQueuedWorkflow(byOldCommitId) == null && getWorkflow(byOldCommitId) == null) {
1✔
197
            createQueuedWorkflow(byOldCommitId);
×
198
          }
199

200
          workflow = null;
1✔
201
        } catch (Exception e) {
×
202
          // Add back the old workflow if it is broken now
NEW
203
          logger.error("Could not parse updated workflow " + workflow.getId());
×
204
          workflowRepository.save(workflow);
×
205
        }
1✔
206
      }
207
    }
208

209
    return workflow;
1✔
210
  }
211

212
  /**
213
   * Get a list of workflows from a directory
214
   *
215
   * @param gitInfo The Git directory information
216
   * @return The list of workflow overviews
217
   */
218
  public List<WorkflowOverview> getWorkflowsFromDirectory(GitDetails gitInfo)
219
      throws IOException, GitAPIException {
220
    List<WorkflowOverview> workflowsInDir = new ArrayList<>();
1✔
221
    Git repo = null;
1✔
222
    try {
223
      boolean safeToAccess = gitSemaphore.acquire(gitInfo.getRepoUrl());
1✔
224
      while (repo == null) {
1✔
225
        try {
226
          repo = gitService.getRepository(gitInfo, safeToAccess);
1✔
227
        } catch (RefNotFoundException ex) {
×
228
          // Attempt slashes in branch fix
229
          GitDetails correctedForSlash = gitService.transferPathToBranch(gitInfo);
×
230
          if (correctedForSlash != null) {
×
231
            gitInfo = correctedForSlash;
×
232
          } else {
233
            throw ex;
×
234
          }
235
        }
1✔
236
      }
237

238
      Path localPath = repo.getRepository().getWorkTree().toPath();
1✔
239
      Path pathToDirectory = localPath.resolve(gitInfo.getPath()).normalize().toAbsolutePath();
1✔
240
      Path root = Paths.get("/").toAbsolutePath();
1✔
241
      if (pathToDirectory.equals(root)) {
1✔
242
        pathToDirectory = localPath;
1✔
243
      } else if (!pathToDirectory.startsWith(localPath.normalize().toAbsolutePath())) {
×
244
        // Prevent path traversal attacks
245
        throw new WorkflowNotFoundException();
×
246
      }
247

248
      File directory = new File(pathToDirectory.toString());
1✔
249
      if (directory.exists() && directory.isDirectory()) {
1✔
250
        for (final File file : Objects.requireNonNull(directory.listFiles())) {
1✔
251
          int eIndex = file.getName().lastIndexOf('.') + 1;
1✔
252
          if (eIndex > 0) {
1✔
253
            String extension = file.getName().substring(eIndex);
1✔
254
            if (extension.equals("cwl")) {
1✔
255
              try {
256
                WorkflowOverview overview = cwlService.getWorkflowOverview(file);
1✔
257
                if (overview != null) {
1✔
258
                  workflowsInDir.add(overview);
1✔
259
                }
260
              } catch (IOException err) {
×
NEW
261
                logger.error("Skipping file due to IOException: " + file, err);
×
262
              }
1✔
263
            }
264
          }
265
        }
266
      }
267
    } finally {
268
      gitSemaphore.release(gitInfo.getRepoUrl());
1✔
269
      FileUtils.deleteTemporaryGitRepository(repo);
1✔
270
    }
271
    return workflowsInDir;
1✔
272
  }
273

274
  /**
275
   * Get the RO bundle for a Workflow, triggering re-download if it does not exist
276
   *
277
   * @param gitDetails The origin details of the workflow
278
   * @return The file containing the RO bundle
279
   * @throws ROBundleNotFoundException If the RO bundle was not found
280
   */
281
  public File getROBundle(GitDetails gitDetails) throws ROBundleNotFoundException {
282
    // Get workflow from database
283
    Workflow workflow = getWorkflow(gitDetails);
1✔
284

285
    // If workflow does not exist or the bundle doesn't yet
286
    if (workflow == null || workflow.getRoBundlePath() == null) {
1✔
287
      throw new ROBundleNotFoundException();
×
288
    }
289

290
    // 404 error with retry if the file on disk does not exist
291
    File bundleDownload = new File(workflow.getRoBundlePath());
1✔
292
    if (!bundleDownload.exists()) {
1✔
293
      // Clear current RO bundle link and create a new one (async)
294
      workflow.setRoBundlePath(null);
×
295
      workflowRepository.save(workflow);
×
296
      generateROBundle(workflow);
×
297
      throw new ROBundleNotFoundException();
×
298
    }
299

300
    return bundleDownload;
1✔
301
  }
302

303
  /**
304
   * Builds a new queued workflow from Git
305
   *
306
   * @param gitInfo Git information for the workflow
307
   * @return A queued workflow model
308
   * @throws GitAPIException Git errors
309
   * @throws WorkflowNotFoundException Workflow was not found within the repository
310
   * @throws IOException Other file handling exceptions
311
   */
312
  public QueuedWorkflow createQueuedWorkflow(GitDetails gitInfo)
313
      throws GitAPIException, WorkflowNotFoundException, IOException, CWLValidationException {
314
    QueuedWorkflow queuedWorkflow;
315

316
    Git repo = null;
1✔
317
    try {
318
      boolean safeToAccess = gitSemaphore.acquire(gitInfo.getRepoUrl());
1✔
319
      while (repo == null) {
1✔
320
        try {
321
          repo = gitService.getRepository(gitInfo, safeToAccess);
1✔
322
        } catch (RefNotFoundException ex) {
×
323
          // Attempt slashes in branch fix
324
          GitDetails correctedForSlash = gitService.transferPathToBranch(gitInfo);
×
325
          if (correctedForSlash != null) {
×
326
            gitInfo = correctedForSlash;
×
327
          } else {
328
            throw ex;
×
329
          }
330
        }
1✔
331
      }
332
      Path localPath = repo.getRepository().getWorkTree().toPath();
1✔
333
      String latestCommit = gitService.getCurrentCommitID(repo);
1✔
334

335
      Path workflowFile = localPath.resolve(gitInfo.getPath()).normalize().toAbsolutePath();
1✔
336
      // Prevent path traversal attacks
337
      if (!workflowFile.startsWith(localPath.normalize().toAbsolutePath())) {
1✔
338
        throw new WorkflowNotFoundException();
×
339
      }
340

341
      // Check workflow is readable
342
      if (!Files.isReadable(workflowFile)) {
1✔
343
        throw new WorkflowNotFoundException("Unable to read workflow file on disk.");
×
344
      }
345

346
      // Handling of packed workflows
347
      String packedWorkflowId = gitInfo.getPackedId();
1✔
348
      if (packedWorkflowId == null) {
1✔
349
        if (cwlService.isPacked(workflowFile.toFile())) {
1✔
350
          List<WorkflowOverview> overviews =
×
351
              cwlService.getWorkflowOverviewsFromPacked(workflowFile.toFile());
×
NEW
352
          if (overviews.isEmpty()) {
×
353
            throw new IOException(
×
354
                "No workflow was found within the packed CWL file. " + gitInfo.toSummary());
×
355
          } else {
356
            // Dummy queued workflow object to return the list
357
            QueuedWorkflow overviewList = new QueuedWorkflow();
×
358
            overviewList.setWorkflowList(overviews);
×
359
            return overviewList;
×
360
          }
361
        }
362
      } else {
363
        // Packed ID specified but was not found
364
        if (!cwlService.isPacked(workflowFile.toFile())) {
×
365
          throw new WorkflowNotFoundException();
×
366
        }
367
      }
368

369
      Workflow basicModel = cwlService.parseWorkflowNative(workflowFile, packedWorkflowId);
1✔
370

371
      // Set origin details
372
      basicModel.setRetrievedOn(new Date());
1✔
373
      basicModel.setRetrievedFrom(gitInfo);
1✔
374
      basicModel.setLastCommit(latestCommit);
1✔
375

376
      // Save the queued workflow to database
377
      queuedWorkflow = new QueuedWorkflow();
1✔
378
      queuedWorkflow.setTempRepresentation(basicModel);
1✔
379
      queuedWorkflowRepository.save(queuedWorkflow);
1✔
380

381
      // ASYNC OPERATIONS
382
      // Parse with cwltool and update model
383
      try {
384
        cwlToolRunner.createWorkflowFromQueued(queuedWorkflow);
1✔
385
      } catch (Exception e) {
×
386
        logger.error("Could not update workflow with cwltool: " + gitInfo.toSummary(), e);
×
387
      }
1✔
388

389
    } catch (GitAPIException | RuntimeException | IOException e) {
1✔
390
      logger.warn(
1✔
391
          String.format(
1✔
392
              "Failed to create Queued Workflow: %s - Temporary files will be deleted for %s.",
393
              e.getMessage(), gitInfo.toSummary()),
1✔
394
          e);
395
      FileUtils.deleteGitRepository(repo);
1✔
396
      throw e;
1✔
397
    } finally {
398
      gitSemaphore.release(gitInfo.getRepoUrl());
1✔
399
      FileUtils.deleteTemporaryGitRepository(repo);
1✔
400
    }
401

402
    // Return this model to be displayed
403
    return queuedWorkflow;
1✔
404
  }
405

406
  /**
407
   * Retry the running of cwltool to create a new workflow
408
   *
409
   * @param queuedWorkflow The workflow to use to update
410
   */
411
  public void retryCwltool(QueuedWorkflow queuedWorkflow) {
412
    queuedWorkflow.setMessage(null);
×
413
    queuedWorkflow.setCwltoolStatus(CWLToolStatus.RUNNING);
×
414
    queuedWorkflowRepository.save(queuedWorkflow);
×
415
    try {
416
      cwlToolRunner.createWorkflowFromQueued(queuedWorkflow);
×
417
    } catch (Exception e) {
×
418
      logger.error("Could not update workflow " + queuedWorkflow.getId() + " with cwltool.", e);
×
419
    }
×
420
  }
×
421

422
  /**
423
   * Find a workflow by commit ID and path
424
   *
425
   * @param commitID The commit ID of the workflow
426
   * @param path The path to the workflow within the repository
427
   * @return A workflow model with the above two parameters
428
   */
429
  public Workflow findByCommitAndPath(String commitID, String path)
430
      throws WorkflowNotFoundException {
431
    return findByCommitAndPath(commitID, path, Optional.empty());
×
432
  }
433

434
  /**
435
   * Find a workflow by commit ID and path
436
   *
437
   * @param commitID The commit ID of the workflow
438
   * @param path The path to the workflow within the repository
439
   * @param part The #part within the workflow, or Optional.empty() for no part, or <code>null
440
   *     </code> for any part
441
   * @return A workflow model with the above two parameters
442
   */
443
  public Workflow findByCommitAndPath(String commitID, String path, Optional<String> part)
444
      throws WorkflowNotFoundException, MultipleWorkflowsException {
445
    List<Workflow> matches = workflowRepository.findByCommitAndPath(commitID, path);
×
446
    if (matches == null || matches.isEmpty()) {
×
447
      throw new WorkflowNotFoundException();
×
NEW
448
    } else if (part.isEmpty()) {
×
449
      // any part will do - so we'll pick the first one
NEW
450
      return matches.getFirst();
×
451
    } else {
452
      // Multiple matches means either added by both branch and ID
453
      // Or packed workflow
454
      for (Workflow workflow : matches) {
×
455
        if (Objects.equals(part.orElse(null), workflow.getRetrievedFrom().getPackedId())) {
×
456
          // either both are null; or both are non-null and equal
457
          return workflow;
×
458
        }
459
      }
×
460
      // Sorry, don't know about your part!
NEW
461
      throw new WorkflowNotFoundException();
×
462
    }
463
  }
464

465
  /**
466
   * Get a graph in a particular format and return it
467
   *
468
   * @param format The format for the graph file
469
   * @param gitDetails The Git details of the workflow
470
   * @return A FileSystemResource representing the graph
471
   * @throws WorkflowNotFoundException Error getting the workflow or format
472
   * @throws IOException Error reading the workflow files
473
   */
474
  public FileSystemResource getWorkflowGraph(String format, GitDetails gitDetails)
475
      throws WorkflowNotFoundException, IOException {
476
    // Determine file extension from format
477
    String extension =
NEW
478
        switch (format) {
×
NEW
479
          case "svg", "png" -> format;
×
NEW
480
          case "xdot" -> "dot";
×
NEW
481
          default -> throw new WorkflowNotFoundException("Format " + format + " not recognized.");
×
482
        };
483

484
    // Get workflow
485
    Workflow workflow = getWorkflow(gitDetails);
×
486
    if (workflow == null) {
×
487
      throw new WorkflowNotFoundException(
×
488
          "Unable to retrieve workflow for " + gitDetails.toSummary());
×
489
    }
490

491
    // Generate graph and serve the file
492
    Path out =
×
493
        graphVizService.getGraphPath(
×
NEW
494
            workflow.getId() + "." + extension, workflow.getVisualisationDot(), format);
×
NEW
495
    return new FileSystemResource(out.toString());
×
496
  }
497

498
  /**
499
   * Generates the RO bundle for a Workflow and adds it to the model
500
   *
501
   * @param workflow The workflow model to create a Research Object for
502
   */
503
  private void generateROBundle(Workflow workflow) {
504
    try {
505
      ROBundleFactory.createWorkflowRO(workflow);
×
506
    } catch (Exception ex) {
×
507
      logger.error(
×
508
          "Error creating RO Bundle for workflow from " + workflow.getRetrievedFrom().toSummary(),
×
509
          ex);
510
    }
×
511
  }
×
512

513
  /**
514
   * Removes a workflow and its research object bundle
515
   *
516
   * @param workflow The workflow to be deleted
517
   */
518
  private void removeWorkflow(Workflow workflow) {
519
    // Delete the Research Object Bundle from disk
520
    if (workflow.getRoBundlePath() != null) {
1✔
521
      File roBundle = new File(workflow.getRoBundlePath());
1✔
522
      if (roBundle.delete()) {
1✔
523
        logger.debug("Deleted Research Object Bundle for workflow " + workflow.getId());
1✔
524
      } else {
NEW
525
        logger.info("Failed to delete Research Object Bundle for workflow " + workflow.getId());
×
526
      }
527
    }
528

529
    // Delete cached graphviz images if they exist
530
    graphVizService.deleteCache(workflow.getId());
1✔
531

532
    // Remove the workflow from the database
533
    workflowRepository.delete(workflow);
1✔
534

535
    // Remove any queued repositories pointing to the workflow
536
    queuedWorkflowRepository.deleteByTempRepresentation_RetrievedFrom(workflow.getRetrievedFrom());
1✔
537
  }
1✔
538

539
  /**
540
   * Check for cache expiration based on time and commit sha
541
   *
542
   * @param workflow The cached workflow model
543
   * @return Whether or not there are new commits
544
   */
545
  private boolean cacheExpired(Workflow workflow) {
546
    // If this is a branch and not added by commit ID
547
    if (!workflow.getRetrievedFrom().getBranch().equals(workflow.getLastCommit())) {
1✔
548
      try {
549
        // Calculate expiration
550
        Calendar expireCal = Calendar.getInstance();
1✔
551
        expireCal.setTime(workflow.getRetrievedOn());
1✔
552
        expireCal.add(Calendar.DATE, cacheDays);
1✔
553
        Date expirationDate = expireCal.getTime();
1✔
554

555
        // Check cached retrievedOn date
556
        if (expirationDate.before(new Date())) {
1✔
557
          // Cache expiry time has elapsed
558
          // Check current head of the branch with the cached head
559
          logger.info(
1✔
560
              "Time has expired for caching, checking commits for workflow " + workflow.getId());
1✔
561
          String currentHead;
562
          Git repo = null;
1✔
563
          boolean safeToAccess = gitSemaphore.acquire(workflow.getRetrievedFrom().getRepoUrl());
1✔
564
          try {
565
            repo = gitService.getRepository(workflow.getRetrievedFrom(), safeToAccess);
1✔
566
            currentHead = gitService.getCurrentCommitID(repo);
1✔
567
          } finally {
568
            gitSemaphore.release(workflow.getRetrievedFrom().getRepoUrl());
1✔
569
            FileUtils.deleteTemporaryGitRepository(repo);
1✔
570
          }
571
          logger.info(
1✔
572
              "Current: "
573
                  + workflow.getLastCommit()
1✔
574
                  + ", HEAD: "
575
                  + currentHead
576
                  + " for workflow "
577
                  + workflow.getId());
1✔
578

579
          // Reset date in database if there are still no changes
580
          boolean expired = !workflow.getLastCommit().equals(currentHead);
1✔
581
          if (!expired) {
1✔
582
            workflow.setRetrievedOn(new Date());
×
583
            workflowRepository.save(workflow);
×
584
          }
585

586
          // Return whether the cache has expired
587
          return expired;
1✔
588
        }
589
      } catch (Exception ex) {
×
590
        // Default to no expiry if there was an API error
591
        logger.error(
×
592
            "API Error when checking for latest commit ID for caching for workflow "
NEW
593
                + workflow.getId(),
×
594
            ex);
595
      }
×
596
    }
597
    return false;
1✔
598
  }
599

600
  public Optional<String> findRawBaseForCommit(String commitId) {
601
    for (Workflow w : workflowRepository.findByCommit(commitId)) {
×
602
      String potentialRaw = w.getRetrievedFrom().getRawUrl(commitId);
×
603
      String path = w.getRetrievedFrom().getPath();
×
604
      if (potentialRaw.endsWith(path)) {
×
605
        return Optional.of(potentialRaw.replace(path, ""));
×
606
      }
607
    }
×
608
    // Not found, or not at github.com/gitlab.com
609
    return Optional.empty();
×
610
  }
611
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc