code/go/internal/validator/semantic/validate_changelog_links.go (72 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.
package semantic
import (
"errors"
"fmt"
"net/url"
"path"
"strconv"
"strings"
"github.com/elastic/package-spec/v3/code/go/internal/fspath"
"github.com/elastic/package-spec/v3/code/go/pkg/specerrors"
)
var errGithubIssue = errors.New("issue number in changelog link should be a positive number") // TODO test validationError structuredError
// ChangelogLinkError records the link and the error
type ChangelogLinkError struct {
link string
err error
}
// Is checks if the target matches one of the allowed links errors.
// Currently checks for github issue/pr links.
func (e ChangelogLinkError) Is(target error) bool {
return target == errGithubIssue
}
func (e ChangelogLinkError) Error() string {
return fmt.Sprintf("%s: %s", e.link, e.err.Error())
}
// ValidateChangelogLinks returns validation errors if the link(s) do not have a valid PR github.com link.
// If the link is not a github.com link this validation is skipped and does not return an error.
func ValidateChangelogLinks(fsys fspath.FS) specerrors.ValidationErrors {
changelogLinks, err := readChangelogLinks(fsys)
if err != nil {
return specerrors.ValidationErrors{specerrors.NewStructuredError(err, specerrors.UnassignedCode)}
}
return ensureLinksAreValid(changelogLinks)
}
func readChangelogLinks(fsys fspath.FS) ([]string, error) {
return readChangelog(fsys, `$[*].changes[*].link`)
}
func ensureLinksAreValid(links []string) specerrors.ValidationErrors {
type validateFn func(link *url.URL) error
var errs specerrors.ValidationErrors
validateLinks := []struct {
domain string
validateLink validateFn
}{
{
"github.com",
validateGithubLink,
},
}
for _, link := range links {
linkURL, err := url.Parse(link)
if err != nil {
errs.Append(specerrors.ValidationErrors{
specerrors.NewStructuredErrorf("invalid URL %v", err),
})
continue
}
for _, vl := range validateLinks {
if strings.Contains(linkURL.Host, vl.domain) {
if err = vl.validateLink(linkURL); err != nil {
errs.Append(specerrors.ValidationErrors{specerrors.NewStructuredError(err, specerrors.UnassignedCode)})
}
}
}
}
return errs
}
func validateGithubLink(ghLink *url.URL) error {
prNum, err := strconv.Atoi(path.Base(ghLink.Path))
if err != nil || prNum <= 0 {
return &ChangelogLinkError{
ghLink.String(),
errGithubIssue,
}
}
return nil
}