Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# docsync Releases

## v0.3.1

**Date:** unreleased
**Previous release:** v0.3.0

### Fixed

- DS-SEC accepts a "Latest release" supported-versions row that names the version in parentheses (`Latest release (v1.4.x)`); the version must be the latest minor, as a bare `v1.4.x` must.
- A build-ignored `_test.go` file (`//go:build ignore`) is skipped like a build-ignored non-test file: its test functions no longer count for DS-REF and its imports are no longer recorded.
- The `goapi.ExportedNames` doc comment no longer claims struct fields; it returns funcs, values, types and the methods of exported types.

---

## v0.3.0

**Date:** 2026-11-07
Expand Down
25 changes: 21 additions & 4 deletions internal/check/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func normGo(v string) string {
var (
emailRE = regexp.MustCompile(`[A-Za-z0-9._%+-]+@otfabric\.[a-z]+`)
supportedRE = regexp.MustCompile(`(?i)^v?(\d+)\.(\d+)\.x(?:$|[\s(,;:])`)
latestRE = regexp.MustCompile(`(?i)^(?:the )?(?:latest|current|newest|most recent)(?: (?:release|version|minor))?$`)
latestRE = regexp.MustCompile(`(?i)^(?:the )?(?:latest|current|newest|most recent)(?: (?:release|version|minor))?(?:\s*\(([^()]*)\))?$`)
yesRE = regexp.MustCompile(`^(?:(?i:yes|supported)\b|✓|✅|✔|:white_check_mark:)`)
)

Expand Down Expand Up @@ -144,6 +144,24 @@ func cellText(c string) string {
return strings.TrimSpace(strings.NewReplacer("`", "", "*", "", "_", "").Replace(c))
}

// supportsMinor reports whether ver starts with the minor want ("0.8.x").
func supportsMinor(ver, want string) bool {
m := supportedRE.FindStringSubmatch(ver)
return m != nil && m[1]+"."+m[2]+".x" == want
}

// latestRow reports whether ver is a "Latest release" cell: bare, it names
// the latest minor, whatever it is; with a parenthesised version ("Latest
// release (v1.4.x)") that version must be the latest minor, as a bare
// "v1.4.x" must.
func latestRow(ver, want string) bool {
m := latestRE.FindStringSubmatch(ver)
if m == nil {
return false
}
return !strings.Contains(ver, "(") || supportsMinor(strings.TrimSpace(m[1]), want)
}

// SecurityAddress is the only address for private security reports.
const SecurityAddress = "security@otfabric.io"

Expand Down Expand Up @@ -192,9 +210,8 @@ func Security(r *Repo) []finding.Finding {
continue
}
ver := cellText(row.Cells[0]) // "0.8.x", "0.8.x (latest: v0.8.2)"
m := supportedRE.FindStringSubmatch(ver)
if (m != nil && m[1]+"."+m[2]+".x" == want || latestRE.MatchString(ver) || openEnded(ver, latest)) && yesRE.MatchString(strings.TrimSpace(row.Cells[1])) {
found = true // "Latest release | Yes" names the latest minor, whatever it is
if (supportsMinor(ver, want) || latestRow(ver, want) || openEnded(ver, latest)) && yesRE.MatchString(strings.TrimSpace(row.Cells[1])) {
found = true
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions internal/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,20 @@ func TestGitChecks(t *testing.T) {
if _, out, _ := run(t, "check", dir); !strings.Contains(out, "does not support 0.3.x") {
t.Errorf("0.3.xy is not 0.3.x:\n%s", out)
}
// A "Latest release" row may name the version in parentheses; it must
// then be the latest minor.
for _, cell := range []string{"Latest release (v0.3.x)", "Latest release (0.3.x)", "Latest release(v0.3.x)"} {
writeFile(t, dir, "SECURITY.md", "Report to security@otfabric.io.\n\n| Version | Supported |\n|---|---|\n| "+cell+" | Yes |\n")
if code, out, _ := run(t, "check", dir); code != ExitOK {
t.Errorf("%s: exit %d\n%s", cell, code, out)
}
}
for _, cell := range []string{"Latest release (v0.2.x)", "Latest release (v0.3.xy)", "Latest release (soon)", "Latest release ()"} {
writeFile(t, dir, "SECURITY.md", "Report to security@otfabric.io.\n\n| Version | Supported |\n|---|---|\n| "+cell+" | Yes |\n")
if _, out, _ := run(t, "check", dir); !strings.Contains(out, "does not support 0.3.x") {
t.Errorf("%s does not name 0.3.x:\n%s", cell, out)
}
}
// A "Latest release" row supports whichever minor is the latest.
writeFile(t, dir, "SECURITY.md", "Report to security@otfabric.io.\n\n| Version | Supported |\n|---|---|\n| Latest release | Yes |\n| Older releases | No |\n")
if code, out, _ := run(t, "check", dir); code != ExitOK {
Expand Down
9 changes: 5 additions & 4 deletions internal/goapi/goapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ var testFuncRE = regexp.MustCompile(`^(?:Test|Fuzz|Benchmark|Example)(?:$|[^a-z]

// addTests records the test functions of a _test.go file.
func (m *Module) addTests(fset *token.FileSet, p string) {
f, err := parser.ParseFile(fset, p, nil, parser.SkipObjectResolution)
f, err := parser.ParseFile(fset, p, nil, parser.SkipObjectResolution|parser.ParseComments) // ignored reads the comments
if err != nil || ignored(f) {
return
}
Expand Down Expand Up @@ -547,9 +547,10 @@ func NewIndex(m *Module) *Index {
return ix
}

// ExportedNames returns the exported top-level names (and the methods and
// fields of exported types) of the package in dir, a dependency's: its
// non-test Go files, whatever their build constraints.
// ExportedNames returns the exported top-level names (funcs, values and
// types) and the methods of exported types of the package in dir, a
// dependency's: its non-test Go files, whatever their build constraints.
// Struct fields are not included.
func ExportedNames(dir string) map[string]bool {
out := map[string]bool{}
files, _ := filepath.Glob(filepath.Join(dir, "*.go"))
Expand Down
15 changes: 14 additions & 1 deletion internal/goapi/goapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ import "testing"
func TestExt(t *testing.T) {}
`,
"bad_test.go": "package m\n\nfunc TestBroken( {\n",
// A build-ignored test file is not part of the package's tests.
"ignored_test.go": `//go:build ignore

package m

import (
"testing"

"github.com/dep/ignoredonly"
)

func TestIgnored(t *testing.T) { _ = ignoredonly.X }
`,
})
m, err := Load(dir)
if err != nil {
Expand Down Expand Up @@ -165,7 +178,7 @@ type I interface{ IM() }
t.Errorf("%s is missing from %v", n, got)
}
}
for _, n := range []string{"unexported", "f", "TestOnly", "X", "Broken"} {
for _, n := range []string{"unexported", "f", "TestOnly", "X", "Broken", "Field"} { // no struct fields
if got[n] {
t.Errorf("%s is in %v", n, got)
}
Expand Down
Loading