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

common-workflow-language / cwlviewer / #1998

13 May 2026 06:03PM UTC coverage: 70.05% (-0.3%) from 70.334%
#1998

Pull #751

github

kinow
Remove custom implementations with native queries, now Hibernate + JPA works!
Pull Request #751: Bump org.springframework.boot:spring-boot-starter-parent from 3.1.4 to 4.1.0-RC1

117 of 194 new or added lines in 30 files covered. (60.31%)

20 existing lines in 4 files now uncovered.

1691 of 2414 relevant lines covered (70.05%)

0.7 hits per line

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

65.71
/src/main/java/org/commonwl/view/git/GitDetails.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.git;
21

22
import com.fasterxml.jackson.annotation.JsonCreator;
23
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
24
import com.fasterxml.jackson.annotation.JsonProperty;
25
import java.io.IOException;
26
import java.io.Serializable;
27
import java.net.URI;
28
import java.net.URISyntaxException;
29
import java.nio.file.Path;
30
import java.util.Objects;
31
import org.commonwl.view.util.LicenseUtils;
32
import org.slf4j.Logger;
33
import org.slf4j.LoggerFactory;
34
import tools.jackson.databind.JsonNode;
35
import tools.jackson.databind.ObjectMapper;
36

37
/** Represents all the parameters necessary to access a file/directory with Git */
38
@JsonIgnoreProperties(
39
    value = {"internalUrl", "logger"},
40
    ignoreUnknown = true)
41
public class GitDetails implements Serializable {
42

43
  private final Logger logger = LoggerFactory.getLogger(this.getClass());
1✔
44

45
  private String repoUrl;
46
  private String branch;
47
  private String path;
48
  private String packedId;
49

NEW
50
  public GitDetails() {}
×
51

52
  @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
53
  public GitDetails(
54
      @JsonProperty("repoUrl") String repoUrl,
55
      @JsonProperty("branch") String branch,
56
      @JsonProperty("path") String path) {
1✔
57
    this.repoUrl = repoUrl;
1✔
58

59
    // Default to the master branch
60
    if (branch == null || branch.isEmpty()) {
1✔
61
      // TODO: get default branch name for this rather than assuming master
62
      this.branch = "master";
1✔
63
    } else {
64
      this.branch = branch;
1✔
65
    }
66

67
    // Default to root path
68
    setPath(path);
1✔
69
  }
1✔
70

71
  public String getRepoUrl() {
72
    return repoUrl;
1✔
73
  }
74

75
  @SuppressWarnings("unused")
76
  public void setRepoUrl(String repoUrl) {
77
    this.repoUrl = repoUrl;
×
78
  }
×
79

80
  public String getBranch() {
81
    return branch;
1✔
82
  }
83

84
  public void setBranch(String branch) {
85
    if (branch == null || branch.isEmpty()) {
1✔
NEW
86
      this.branch = "master";
×
87
    } else {
88
      this.branch = branch;
1✔
89
    }
90
  }
1✔
91

92
  public String getPackedId() {
93
    return packedId;
1✔
94
  }
95

96
  public void setPackedId(String packedId) {
97
    this.packedId = packedId;
1✔
98
  }
1✔
99

100
  public String getPath() {
101
    return path;
1✔
102
  }
103

104
  public void setPath(String path) {
105
    if (path == null || path.isEmpty()) {
1✔
106
      this.path = "/";
1✔
107
    } else if (path.startsWith("/") && path.length() > 1) {
1✔
108
      this.path = path.substring(1);
×
109
    } else {
110
      this.path = path;
1✔
111
    }
112
  }
1✔
113

114
  /**
115
   * Get the type of repository URL this object refers to
116
   *
117
   * @return The type for the URL
118
   */
119
  public GitType getType() {
120
    try {
121
      URI uri = new URI(repoUrl);
1✔
122
      String domain = uri.getHost();
1✔
123
      if (domain.startsWith("www.")) {
1✔
124
        domain = domain.substring(4);
×
125
      }
126
      return switch (domain) {
1✔
127
        case "github.com" -> GitType.GITHUB;
1✔
128
        case "gitlab.com" -> GitType.GITLAB;
1✔
129
        case "bitbucket.org" -> GitType.BITBUCKET;
1✔
130
        default -> GitType.GENERIC;
1✔
131
      };
132
    } catch (URISyntaxException ex) {
×
133
      return GitType.GENERIC;
×
134
    }
135
  }
136

137
  /**
138
   * Get the URL to the external resource representing this workflow
139
   *
140
   * @param branchOverride The branch to use instead of the one in this instance
141
   * @return The URL
142
   */
143
  public String getUrl(String branchOverride) {
144
    String packedPart = packedId == null ? "" : "#" + packedId;
1✔
145
    return switch (getType()) {
1✔
146
      case GITHUB, GITLAB ->
147
          "https://"
1✔
148
              + normaliseUrl(repoUrl).replace(".git", "")
1✔
149
              + "/blob/"
150
              + branchOverride
151
              + "/"
152
              + path
153
              + packedPart;
154
      case BITBUCKET ->
155
          "https://"
1✔
156
              + normaliseUrl(repoUrl).replace(".git", "")
1✔
157
              + "/src/"
158
              + branchOverride
159
              + "/"
160
              + path
161
              + packedPart;
162
      default -> repoUrl;
1✔
163
    };
164
  }
165

166
  /**
167
   * Get the URL to the external resource representing this workflow
168
   *
169
   * @return The URL
170
   */
171
  public String getUrl() {
172
    return getUrl(branch);
1✔
173
  }
174

175
  /**
176
   * Get the URL to the page containing this workflow
177
   *
178
   * @param branchOverride The branch to use instead of the one in this instance
179
   * @return The URL
180
   */
181
  public String getInternalUrl(String branchOverride) {
182
    String packedPart = packedId == null ? "" : "%23" + packedId;
1✔
183

184
    // TODO: Maybe this needs encoding to support commas? See issue #782
185
    String pathPart = path.equals("/") ? "" : "/" + path;
1✔
186

187
    return switch (getType()) {
1✔
188
      case GITHUB, GITLAB ->
189
          "/workflows/"
1✔
190
              + normaliseUrl(repoUrl).replace(".git", "")
1✔
191
              + "/blob/"
192
              + branchOverride
193
              + pathPart
194
              + packedPart;
195

196
      default ->
197
          "/workflows/" + normaliseUrl(repoUrl) + "/" + branchOverride + pathPart + packedPart;
1✔
198
    };
199
  }
200

201
  /**
202
   * Get the URL to the page containing this workflow
203
   *
204
   * @return The URL
205
   */
206
  public String getInternalUrl() {
207
    return getInternalUrl(branch);
1✔
208
  }
209

210
  /**
211
   * Get the URL directly to the resource
212
   *
213
   * @param branchOverride The branch to use, or <code>null</code> to use this instance's branch
214
   * @param pathOverride The path to use, or <code>null</code> to use this instance's path
215
   * @return The URL
216
   */
217
  public String getRawUrl(String branchOverride, String pathOverride) {
218
    if (branchOverride == null) {
1✔
UNCOV
219
      branchOverride = branch;
×
220
    }
221
    if (pathOverride == null) {
1✔
222
      pathOverride = path;
×
223
    }
224
    return switch (getType()) {
1✔
225
      case GITHUB ->
226
          "https://raw.githubusercontent.com/"
1✔
227
              + normaliseUrl(repoUrl).replace("github.com/", "").replace(".git", "")
1✔
228
              + "/"
229
              + branchOverride
230
              + "/"
231
              + pathOverride;
232
      case GITLAB, BITBUCKET ->
233
          "https://"
1✔
234
              + normaliseUrl(repoUrl).replace(".git", "")
1✔
235
              + "/raw/"
236
              + branchOverride
237
              + "/"
238
              + pathOverride;
239
      default -> repoUrl;
1✔
240
    };
241
  }
242

243
  /**
244
   * Get the URL directly to the resource
245
   *
246
   * @param branchOverride The branch to use instead of the one in this instance
247
   * @return The URL
248
   */
249
  public String getRawUrl(String branchOverride) {
250
    return getRawUrl(branchOverride, path);
1✔
251
  }
252

253
  /**
254
   * Get the URL directly to the resource
255
   *
256
   * @return The URL
257
   */
258
  public String getRawUrl() {
259
    return getRawUrl(branch);
1✔
260
  }
261

262
  /**
263
   * Normalises the URL removing protocol and www.
264
   *
265
   * @param url The URL to be normalised
266
   * @return The normalised URL
267
   */
268
  public static String normaliseUrl(String url) {
269
    return url.replace("http://", "")
1✔
270
        .replace("https://", "")
1✔
271
        .replace("ssh://", "")
1✔
272
        .replace("git://", "")
1✔
273
        .replace("www.", "");
1✔
274
  }
275

276
  @Override
277
  public boolean equals(Object o) {
278
    if (this == o) return true;
1✔
279
    if (o == null || getClass() != o.getClass()) return false;
1✔
280
    GitDetails that = (GitDetails) o;
1✔
281
    return Objects.equals(repoUrl, that.repoUrl)
1✔
282
        && Objects.equals(branch, that.branch)
1✔
283
        && Objects.equals(path, that.path)
1✔
284
        && Objects.equals(packedId, that.packedId);
1✔
285
  }
286

287
  @Override
288
  public int hashCode() {
289
    return Objects.hash(repoUrl, branch, path, packedId);
1✔
290
  }
291

292
  public String toSummary() {
293
    return String.format(
1✔
294
        "repoUrl: %s branch: %s path: %s packedId: %s", repoUrl, branch, path, packedId);
295
  }
296

297
  /**
298
   * Retrieves license details from the repo, if present.
299
   *
300
   * @param workTree the path to the locally cloned repo
301
   * @return The license URI
302
   */
303
  public String getLicense(Path workTree) throws GitLicenseException {
304
    try {
UNCOV
305
      String[] command = {"licensee", "detect", "--json", workTree.toString()};
×
UNCOV
306
      if (logger.isTraceEnabled()) {
×
NEW
307
        logger.trace("Calling {}", String.join(" ", command));
×
308
      }
UNCOV
309
      Process process = Runtime.getRuntime().exec(command, null);
×
UNCOV
310
      ObjectMapper mapper = new ObjectMapper();
×
UNCOV
311
      JsonNode jsonLicenses = mapper.readTree(process.getInputStream());
×
UNCOV
312
      if (logger.isTraceEnabled()) {
×
313
        logger.trace(
×
NEW
314
            "Licensee retrieved the following licenses:\n{}", jsonLicenses.toPrettyString());
×
315
      }
UNCOV
316
      int size = jsonLicenses.withArray("licenses").size();
×
UNCOV
317
      if (size > 0) {
×
UNCOV
318
        String licenseCandidate =
×
NEW
319
            jsonLicenses.withArray("matched_files").get(0).get("filename").asString();
×
UNCOV
320
        String licenseLink = getRawUrl(null, licenseCandidate);
×
UNCOV
321
        if (logger.isWarnEnabled() && size > 1) {
×
UNCOV
322
          logger.warn(
×
323
              "There are {} identified license files in the {} repository. Taking the first one: {}",
NEW
324
              size,
×
325
              repoUrl,
326
              licenseLink);
327
        }
NEW
328
        String key = jsonLicenses.withArray("licenses").get(0).get("key").asString();
×
UNCOV
329
        if (!"other".equals(key)) {
×
UNCOV
330
          return LicenseUtils.SPDX_LICENSES_PREFIX
×
NEW
331
              + jsonLicenses.withArray("licenses").get(0).get("spdx_id").asString();
×
332
        } else {
UNCOV
333
          return licenseLink;
×
334
        }
335
      } else {
UNCOV
336
        return null;
×
337
      }
338
    } catch (IOException e) {
×
339
      throw new GitLicenseException(
×
340
          "While attempting to detect license for " + workTree + ": " + e.getMessage(), e);
×
341
    }
342
  }
343
}
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