From 30bcb95350097b322c79e1e16a886a22f3dda42b Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Thu, 20 Jun 2024 14:41:43 +0400 Subject: [PATCH] refactor: use version-specific URLs for documentation references (#6966) Signed-off-by: knqyf263 --- pkg/cloud/aws/commands/run.go | 5 +- pkg/commands/artifact/run.go | 28 ++------- pkg/commands/artifact/run_test.go | 48 --------------- pkg/db/db.go | 4 +- pkg/k8s/commands/run.go | 5 +- pkg/version/app/version.go | 4 +- pkg/version/doc/doc.go | 49 +++++++++++++++ pkg/version/doc/doc_test.go | 96 ++++++++++++++++++++++++++++++ pkg/vulnerability/vulnerability.go | 4 +- 9 files changed, 165 insertions(+), 78 deletions(-) delete mode 100644 pkg/commands/artifact/run_test.go create mode 100644 pkg/version/doc/doc.go create mode 100644 pkg/version/doc/doc_test.go diff --git a/pkg/cloud/aws/commands/run.go b/pkg/cloud/aws/commands/run.go index b9a1bbb2bfce..fac103feaf22 100644 --- a/pkg/cloud/aws/commands/run.go +++ b/pkg/cloud/aws/commands/run.go @@ -3,6 +3,7 @@ package commands import ( "context" "errors" + "fmt" "slices" "sort" "strings" @@ -20,6 +21,7 @@ import ( "github.com/aquasecurity/trivy/pkg/flag" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/types" + "github.com/aquasecurity/trivy/pkg/version/doc" ) var allSupportedServicesFunc = awsScanner.AllSupportedServices @@ -140,7 +142,8 @@ func Run(ctx context.Context, opt flag.Options) error { var err error defer func() { if errors.Is(err, context.DeadlineExceeded) { - log.Warn("Provide a higher timeout value, see https://aquasecurity.github.io/trivy/latest/docs/configuration/") + // e.g. https://aquasecurity.github.io/trivy/latest/docs/configuration/ + log.WarnContext(ctx, fmt.Sprintf("Provide a higher timeout value, see %s", doc.URL("/docs/configuration/", ""))) } }() diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 30058ff6c4cb..b430fdc1efc9 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -11,7 +11,6 @@ import ( "github.com/spf13/viper" "golang.org/x/xerrors" - "github.com/aquasecurity/go-version/pkg/semver" "github.com/aquasecurity/trivy-db/pkg/db" tcache "github.com/aquasecurity/trivy/pkg/cache" "github.com/aquasecurity/trivy/pkg/commands/operation" @@ -33,6 +32,7 @@ import ( "github.com/aquasecurity/trivy/pkg/scanner" "github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/utils/fsutils" + "github.com/aquasecurity/trivy/pkg/version/doc" ) // TargetKind represents what kind of artifact Trivy scans @@ -46,8 +46,6 @@ const ( TargetImageArchive TargetKind = "archive" TargetSBOM TargetKind = "sbom" TargetVM TargetKind = "vm" - - devVersion = "dev" ) var ( @@ -397,7 +395,8 @@ func Run(ctx context.Context, opts flag.Options, targetKind TargetKind) (err err defer func() { if errors.Is(err, context.DeadlineExceeded) { - log.Warn("Provide a higher timeout value, see https://aquasecurity.github.io/trivy/latest/docs/configuration/") + // e.g. https://aquasecurity.github.io/trivy/latest/docs/configuration/ + log.WarnContext(ctx, fmt.Sprintf("Provide a higher timeout value, see %s", doc.URL("/docs/configuration/", ""))) } }() @@ -593,10 +592,10 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi // Do not load config file for secret scanning if opts.Scanners.Enabled(types.SecretScanner) { - ver := canonicalVersion(opts.AppVersion) log.Info("Secret scanning is enabled") log.Info("If your scanning is slow, please try '--scanners vuln' to disable secret scanning") - log.Infof("Please see also https://aquasecurity.github.io/trivy/%s/docs/scanner/secret/#recommendation for faster secret detection", ver) + // e.g. https://aquasecurity.github.io/trivy/latest/docs/scanner/secret/#recommendation + log.Infof("Please see also %s for faster secret detection", doc.URL("/docs/scanner/secret/", "recommendation")) } else { opts.SecretConfigPath = "" } @@ -694,20 +693,3 @@ func scan(ctx context.Context, opts flag.Options, initializeScanner InitializeSc } return report, nil } - -func canonicalVersion(ver string) string { - if ver == devVersion { - return ver - } - v, err := semver.Parse(ver) - if err != nil { - return devVersion - } - // Replace pre-release with "dev" - // e.g. v0.34.0-beta1+snapshot-1 - if v.IsPreRelease() || v.Metadata() != "" { - return devVersion - } - // Add "v" prefix and cut a patch number, "0.34.0" => "v0.34" for the url - return fmt.Sprintf("v%d.%d", v.Major(), v.Minor()) -} diff --git a/pkg/commands/artifact/run_test.go b/pkg/commands/artifact/run_test.go deleted file mode 100644 index 02d35a53d44b..000000000000 --- a/pkg/commands/artifact/run_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package artifact - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestCanonicalVersion(t *testing.T) { - tests := []struct { - title string - input string - want string - }{ - { - title: "good way", - input: "0.34.0", - want: "v0.34", - }, - { - title: "version with v - isn't right semver version", - input: "v0.34.0", - want: devVersion, - }, - { - title: "dev version", - input: devVersion, - want: devVersion, - }, - { - title: "pre-release", - input: "v0.34.0-beta1+snapshot-1", - want: devVersion, - }, - { - title: "no version", - input: "", - want: devVersion, - }, - } - - for _, test := range tests { - t.Run(test.title, func(t *testing.T) { - got := canonicalVersion(test.input) - require.Equal(t, test.want, got) - }) - } -} diff --git a/pkg/db/db.go b/pkg/db/db.go index bdb8b34dbb4e..a006404d2c83 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -16,6 +16,7 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/oci" + "github.com/aquasecurity/trivy/pkg/version/doc" ) const ( @@ -188,7 +189,8 @@ func (c *Client) initOCIArtifact(opt types.RegistryOptions) (*oci.Artifact, erro for _, diagnostic := range terr.Errors { // For better user experience if diagnostic.Code == transport.DeniedErrorCode || diagnostic.Code == transport.UnauthorizedErrorCode { - log.Warn("See https://aquasecurity.github.io/trivy/latest/docs/references/troubleshooting/#db") + // e.g. https://aquasecurity.github.io/trivy/latest/docs/references/troubleshooting/#db + log.Warnf("See %s", doc.URL("/docs/references/troubleshooting/", "db")) break } } diff --git a/pkg/k8s/commands/run.go b/pkg/k8s/commands/run.go index 7c34bb4feb2a..6a20d04aee10 100644 --- a/pkg/k8s/commands/run.go +++ b/pkg/k8s/commands/run.go @@ -3,6 +3,7 @@ package commands import ( "context" "errors" + "fmt" "github.com/spf13/viper" "golang.org/x/xerrors" @@ -18,6 +19,7 @@ import ( "github.com/aquasecurity/trivy/pkg/k8s/scanner" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/types" + "github.com/aquasecurity/trivy/pkg/version/doc" ) // Run runs a k8s scan @@ -39,7 +41,8 @@ func Run(ctx context.Context, args []string, opts flag.Options) error { defer func() { cancel() if errors.Is(err, context.DeadlineExceeded) { - log.WarnContext(ctx, "Provide a higher timeout value, see https://aquasecurity.github.io/trivy/latest/docs/configuration/") + // e.g. https://aquasecurity.github.io/trivy/latest/docs/configuration + log.WarnContext(ctx, fmt.Sprintf("Provide a higher timeout value, see %s", doc.URL("/docs/configuration/", ""))) } }() opts.K8sVersion = cluster.GetClusterVersion() diff --git a/pkg/version/app/version.go b/pkg/version/app/version.go index 8a7013078c9a..d1c7bdbe7d3d 100644 --- a/pkg/version/app/version.go +++ b/pkg/version/app/version.go @@ -1,8 +1,6 @@ package app -var ( - ver = "dev" -) +var ver = "dev" func Version() string { return ver diff --git a/pkg/version/doc/doc.go b/pkg/version/doc/doc.go new file mode 100644 index 000000000000..c02dc1e7655a --- /dev/null +++ b/pkg/version/doc/doc.go @@ -0,0 +1,49 @@ +package doc + +import ( + "fmt" + "net/url" + "path" + "strings" + + "github.com/aquasecurity/go-version/pkg/semver" + "github.com/aquasecurity/trivy/pkg/version/app" +) + +const devVersion = "dev" + +// BaseURL returns the base URL for the versioned documentation +func BaseURL(ver string) *url.URL { + ver = canonicalVersion(ver) + return &url.URL{ + Scheme: "https", + Host: "aquasecurity.github.io", + Path: path.Join("trivy", ver), + } +} + +// URL returns the URL for the versioned documentation with the given path +func URL(rawPath, fragment string) string { + base := BaseURL(app.Version()) + base.Path = path.Join(base.Path, rawPath) + base.Fragment = fragment + return base.String() +} + +func canonicalVersion(ver string) string { + if ver == devVersion { + return ver + } + ver = strings.TrimPrefix(ver, "v") + v, err := semver.Parse(ver) + if err != nil { + return devVersion + } + // Replace pre-release with "dev" + // e.g. v0.34.0-beta1+snapshot-1 + if v.IsPreRelease() || v.Metadata() != "" { + return devVersion + } + // Add "v" prefix and cut a patch number, "0.34.0" => "v0.34" for the URL + return fmt.Sprintf("v%d.%d", v.Major(), v.Minor()) +} diff --git a/pkg/version/doc/doc_test.go b/pkg/version/doc/doc_test.go new file mode 100644 index 000000000000..3107172962eb --- /dev/null +++ b/pkg/version/doc/doc_test.go @@ -0,0 +1,96 @@ +package doc_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/version/doc" +) + +func TestBaseURL(t *testing.T) { + tests := []struct { + name string + ver string + want string + }{ + { + name: "dev", + ver: "dev", + want: "https://aquasecurity.github.io/trivy/dev", + }, + { + name: "semver", + ver: "0.52.0", + want: "https://aquasecurity.github.io/trivy/v0.52", + }, + { + name: "with v prefix", + ver: "v0.52.0", + want: "https://aquasecurity.github.io/trivy/v0.52", + }, + { + name: "pre-release", + ver: "0.52.0-beta1", + want: "https://aquasecurity.github.io/trivy/dev", + }, + { + name: "non-semver", + ver: "1", + want: "https://aquasecurity.github.io/trivy/dev", + }, + { + name: "empty", + ver: "", + want: "https://aquasecurity.github.io/trivy/dev", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := doc.BaseURL(tt.ver) + require.Equal(t, tt.want, got.String()) + }) + } +} + +func TestURL(t *testing.T) { + tests := []struct { + name string + rawPath string + fragment string + want string + }{ + { + name: "path without slash", + rawPath: "foo", + want: "https://aquasecurity.github.io/trivy/dev/foo", + }, + { + name: "path with leading slash", + rawPath: "/foo", + want: "https://aquasecurity.github.io/trivy/dev/foo", + }, + { + name: "path with slash", + rawPath: "foo/bar", + want: "https://aquasecurity.github.io/trivy/dev/foo/bar", + }, + { + name: "path with fragment", + rawPath: "foo", + fragment: "bar", + want: "https://aquasecurity.github.io/trivy/dev/foo#bar", + }, + { + name: "empty", + rawPath: "", + want: "https://aquasecurity.github.io/trivy/dev", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := doc.URL(tt.rawPath, tt.fragment) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/vulnerability/vulnerability.go b/pkg/vulnerability/vulnerability.go index a77c93a87a1a..6c1e35427a64 100644 --- a/pkg/vulnerability/vulnerability.go +++ b/pkg/vulnerability/vulnerability.go @@ -12,6 +12,7 @@ import ( "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/types" + "github.com/aquasecurity/trivy/pkg/version/doc" ) var ( @@ -51,7 +52,8 @@ var SuperSet = wire.NewSet( // Show warning if we use severity from another vendor // cf. https://github.com/aquasecurity/trivy/issues/6714 var onceWarn = sync.OnceFunc(func() { - log.Warn("Using severities from other vendors for some vulnerabilities. Read https://aquasecurity.github.io/trivy/latest/docs/scanner/vulnerability/#severity-selection for details.") + // e.g. https://aquasecurity.github.io/trivy/latest/docs/scanner/vulnerability/#severity-selection + log.Warnf("Using severities from other vendors for some vulnerabilities. Read %s for details.", doc.URL("/docs/scanner/vulnerability/", "severity-selection")) }) // Client manipulates vulnerabilities