mirror of
https://github.com/juanfont/headscale.git
synced 2026-10-31 02:34:07 +09:00
A new release arrives as a bump pull request; verify reports a stale set.
162 lines
4.0 KiB
Go
162 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"slices"
|
|
"strings"
|
|
|
|
"golang.org/x/mod/semver"
|
|
)
|
|
|
|
var errPinsDisagree = errors.New("version pins disagree")
|
|
|
|
// finding is one disagreement, phrased so the fix is obvious.
|
|
type finding string
|
|
|
|
// cmdVerify checks that the pins agree with each other and with their
|
|
// upstreams. It is deliberately separate from run: the same checks catch a
|
|
// hand-written commit that breaks a lockstep rule, not just a bad automated one.
|
|
func cmdVerify(ctx context.Context) error {
|
|
r, err := openRepo(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
findings := make([]finding, 0, 4)
|
|
|
|
findings = append(findings, verifyLockstep(ctx, r)...)
|
|
findings = append(findings, verifyBuilders(ctx, r)...)
|
|
findings = append(findings, verifyToolchain(ctx, r)...)
|
|
findings = append(findings, verifyVendorHash(ctx, r)...)
|
|
findings = append(findings, verifyProviders(ctx, r)...)
|
|
|
|
if len(findings) == 0 {
|
|
log.Print("all version pins agree")
|
|
|
|
return nil
|
|
}
|
|
|
|
for _, f := range findings {
|
|
log.Printf("- %s", f)
|
|
}
|
|
|
|
return fmt.Errorf("%w: %d finding(s)", errPinsDisagree, len(findings))
|
|
}
|
|
|
|
func verifyLockstep(ctx context.Context, r *repo) []finding {
|
|
var findings []finding
|
|
|
|
err := checkLockstep(ctx, r)
|
|
if err != nil {
|
|
findings = append(findings, finding(err.Error()))
|
|
}
|
|
|
|
err = checkModComments(r)
|
|
if err != nil {
|
|
findings = append(findings, finding(err.Error()))
|
|
}
|
|
|
|
return findings
|
|
}
|
|
|
|
// verifyBuilders checks the floor relation, not equality: a newer builder
|
|
// compiles an older module, and only the reverse fails.
|
|
func verifyBuilders(ctx context.Context, r *repo) []finding {
|
|
var findings []finding
|
|
|
|
ourMod, err := r.readFile("go.mod")
|
|
if err != nil {
|
|
return []finding{finding(err.Error())}
|
|
}
|
|
|
|
ourGo, err := goDirective(ourMod)
|
|
if err != nil {
|
|
return []finding{finding(err.Error())}
|
|
}
|
|
|
|
tsGo, tsErr := tailscaleGo(ctx)
|
|
|
|
for _, check := range []struct {
|
|
pins []goPin
|
|
floor string
|
|
err error
|
|
}{
|
|
{tailscaleBuilders, tsGo, tsErr},
|
|
{localBuilders, ourGo, nil},
|
|
} {
|
|
if check.err != nil {
|
|
findings = append(findings, finding("could not resolve the tailscale go directive: "+check.err.Error()))
|
|
|
|
continue
|
|
}
|
|
|
|
for _, pin := range check.pins {
|
|
content, err := r.readFile(pin.File)
|
|
if err != nil {
|
|
findings = append(findings, finding(err.Error()))
|
|
|
|
continue
|
|
}
|
|
|
|
have, ok := currentGolangTag(content)
|
|
if !ok {
|
|
findings = append(findings, finding(pin.File+": no golang builder image found"))
|
|
|
|
continue
|
|
}
|
|
|
|
if semver.Compare("v"+have, "v"+check.floor) < 0 {
|
|
findings = append(findings, finding(fmt.Sprintf(
|
|
"%s: golang %s is below the %s required by %s", pin.File, have, check.floor, pin.Why)))
|
|
}
|
|
}
|
|
}
|
|
|
|
return findings
|
|
}
|
|
|
|
// verifyToolchain reports, but never edits, a go directive that has outrun the
|
|
// toolchain nixpkgs ships. Raising it is a promise to downstream packagers, so
|
|
// the decision stays with a human; lowering it is not this tool's call either.
|
|
func verifyToolchain(ctx context.Context, r *repo) []finding {
|
|
err := checkToolchain(ctx, r)
|
|
if err != nil {
|
|
return []finding{finding(err.Error())}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func verifyVendorHash(ctx context.Context, r *repo) []finding {
|
|
out, err := r.nixRun(ctx, "go", "run", "./cmd/vendorhash", "check")
|
|
if err != nil {
|
|
return []finding{finding("flakehashes.json is stale: " + strings.TrimSpace(out))}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// verifyProviders reports a tested provider set that has fallen behind the
|
|
// registry. The tests never resolve it themselves, so nothing else notices.
|
|
func verifyProviders(ctx context.Context, r *repo) []finding {
|
|
have, err := currentProviders(r)
|
|
if err != nil {
|
|
return []finding{finding(err.Error())}
|
|
}
|
|
|
|
want, err := newestProviders(ctx)
|
|
if err != nil {
|
|
return []finding{finding("could not resolve the Terraform provider releases: " + err.Error())}
|
|
}
|
|
|
|
if !slices.Equal(have, want) {
|
|
return []finding{finding(fmt.Sprintf("%s tests Terraform provider %s, but the newest %d releases are %s",
|
|
providerPinFile, strings.Join(have, ", "), testedProviders, strings.Join(want, ", ")))}
|
|
}
|
|
|
|
return nil
|
|
}
|