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

common-workflow-language / cwlviewer / #1992

12 May 2026 05:53PM UTC coverage: 70.861% (+0.5%) from 70.334%
#1992

Pull #751

github

kinow
Build CodeQL with JVM 25
Pull Request #751: Bump org.springframework.boot:spring-boot-starter-parent from 3.1.4 to 4.1.0-RC1

42 of 90 new or added lines in 23 files covered. (46.67%)

19 existing lines in 3 files now uncovered.

1695 of 2392 relevant lines covered (70.86%)

0.71 hits per line

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

66.35
/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
  public void setRepoUrl(String repoUrl) {
76
    this.repoUrl = repoUrl;
×
77
  }
×
78

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

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

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

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

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

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

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

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

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

174
  /**
175
   * Get the URL to the page containing this workflow
176
   *
177
   * @param branchOverride The branch to use instead of the one in this instance
178
   * @return The URL
179
   */
180
  public String getInternalUrl(String branchOverride) {
181
    String packedPart = packedId == null ? "" : "%23" + packedId;
1✔
182
    String pathPart = path.equals("/") ? "" : "/" + path;
1✔
183
    return switch (getType()) {
1✔
184
      case GITHUB, GITLAB ->
185
          "/workflows/"
1✔
186
              + normaliseUrl(repoUrl).replace(".git", "")
1✔
187
              + "/blob/"
188
              + branchOverride
189
              + pathPart
190
              + packedPart;
191
      default ->
192
          "/workflows/" + normaliseUrl(repoUrl) + "/" + branchOverride + pathPart + packedPart;
1✔
193
    };
194
  }
195

196
  /**
197
   * Get the URL to the page containing this workflow
198
   *
199
   * @return The URL
200
   */
201
  public String getInternalUrl() {
202
    return getInternalUrl(branch);
1✔
203
  }
204

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

238
  /**
239
   * Get the URL directly to the resource
240
   *
241
   * @param branchOverride The branch to use instead of the one in this instance
242
   * @return The URL
243
   */
244
  public String getRawUrl(String branchOverride) {
245
    return getRawUrl(branchOverride, path);
1✔
246
  }
247

248
  /**
249
   * Get the URL directly to the resource
250
   *
251
   * @return The URL
252
   */
253
  public String getRawUrl() {
254
    return getRawUrl(branch);
1✔
255
  }
256

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

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

282
  @Override
283
  public int hashCode() {
284
    return Objects.hash(repoUrl, branch, path, packedId);
1✔
285
  }
286

287
  public String toSummary() {
288
    return String.format(
1✔
289
        "repoUrl: %s branch: %s path: %s packedId: %s", repoUrl, branch, path, packedId);
290
  }
291

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