internal/worker: create URL positions for analysis findings
Change-Id: If15c8842b85e7bb23d505a3045b204da74544b1d
Reviewed-on: https://go-review.googlesource.com/c/pkgsite-metrics/+/505736
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Zvonimir Pavlinovic <zpavlinovic@google.com>
diff --git a/internal/worker/analysis.go b/internal/worker/analysis.go
index 4793398..a474648 100644
--- a/internal/worker/analysis.go
+++ b/internal/worker/analysis.go
@@ -194,7 +194,7 @@
row.Version = info.Version
row.CommitTime = info.Time
row.Diagnostics = analysis.JSONTreeToDiagnostics(jsonTree)
- return addSource(row.Diagnostics, 1)
+ return addSource(ctx, row.Diagnostics, 1)
})
if err != nil {
switch {
@@ -281,7 +281,7 @@
// Each diagnostic's position includes a full file path and line number.
// addSource reads the file at the line, and includes nContext lines from above
// and below.
-func addSource(ds []*analysis.Diagnostic, nContext int) error {
+func addSource(ctx context.Context, ds []*analysis.Diagnostic, nContext int) error {
for _, d := range ds {
if d.Position == "" {
// some binaries might collect basic stats, such
@@ -300,6 +300,13 @@
return fmt.Errorf("reading %s:%d: %w", file, line, err)
}
d.Source = bq.NullString{StringVal: source, Valid: true}
+
+ if url, err := sourceURL(d.Position, line); err == nil {
+ d.Position = url
+ } else {
+ // URL creation failure should not result in an error of the analysis run.
+ log.Errorf(ctx, err, "url creation failed for position %s", d.Position)
+ }
}
return nil
}
@@ -328,6 +335,23 @@
return pos[:i], line, col, nil
}
+// sourceURL creates a URL showing the code corresponding to
+// position pos and highlighting line.
+func sourceURL(pos string, line int) (string, error) {
+ // Trim /tmp/modules/ from the position string.
+ relPos := strings.TrimPrefix(pos, modulesDir+"/")
+ if relPos == pos {
+ return "", errors.New("unexpected prefix")
+ }
+ i := strings.IndexByte(relPos, ':')
+ if i < 0 {
+ return "", errors.New("missing colon in position")
+ }
+ path := relPos[:i]
+ return fmt.Sprintf("https://go-mod-viewer.appspot.com/%s#L%d", path, line), nil
+
+}
+
// readSource returns the given line (1-based) from the file, along with
// nContext lines above and below it.
func readSource(file string, line int, nContext int) (_ string, err error) {