blob: 7c3c19b720efbf5c91123e36aab9d17b66d0865f [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
// +build ignore
// This program generates zip_signatures.gen.go.
// The program depends on its own generated file. To regenerate from scratch,
// manually edit the generated file, leaving an empty map literal.
package main
import (
"bytes"
"context"
"flag"
"fmt"
"go/format"
"io/fs"
"io/ioutil"
"log"
"sort"
"text/template"
"time"
"golang.org/x/mod/semver"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/fetch"
"golang.org/x/pkgsite/internal/proxy"
)
var (
verbose = flag.Bool("v", false, "verbose output")
check = flag.Bool("check", false, "check signatures of module@version command-line args")
)
// The list of modules whose exact forks will be excluded from processing. The
// second field is the highest (in the semver sense) without a go.mod file;
// versions with go.mod files are handled by the alternative-module logic.
var largeNoMods = []struct {
modulePath string
highestNoModVersion string
}{
{"github.com/aws/aws-sdk-go", "v1.14.30"},
{"github.com/kubernetes/kubernetes", "v1.15.0-alpha.0"},
{"github.com/Azure/azure-sdk-for-go", "v54.3.0"},
{"github.com/ethereum/go-ethereum", "v1.9.7"},
{"github.com/moby/moby", "v20.10.6"},
{"github.com/influxdata/influxdb", "v1.7.9-dev1"},
{"github.com/etcd-io/etcd", "v3.3.25"},
}
const goFile = "zip_signatures.gen.go"
type sig struct {
Modver internal.Modver
Signature string
}
func main() {
flag.Parse()
ctx := context.Background()
prox, err := proxy.New("https://proxy.golang.org")
if err != nil {
log.Fatal(err)
}
if *check {
err = checkSignatures(ctx, prox, flag.Args())
} else {
err = generateSignatures(ctx, prox)
}
if err != nil {
log.Fatal(err)
}
}
func generateSignatures(ctx context.Context, prox *proxy.Client) error {
// Remember all the module versions we've already computed, to avoid doing so again.
seen := map[internal.Modver]bool{}
for _, mvs := range fetch.ZipSignatures {
for _, mv := range mvs {
seen[mv] = true
}
}
for _, m := range largeNoMods {
// Get all tagged versions of the module.
versions, err := prox.Versions(ctx, m.modulePath)
if err != nil {
return err
}
// Keep versions that are less than or equal to the highest version without a go.mod file.
var noGoModVersions []string
for _, v := range versions {
if semver.Compare(v, m.highestNoModVersion) <= 0 {
noGoModVersions = append(noGoModVersions, v)
}
}
// Compute the signature for each of those versions.
for _, v := range noGoModVersions {
modver := internal.Modver{Path: m.modulePath, Version: v}
if seen[modver] {
if *verbose {
fmt.Printf("%-40s already computed\n", modver)
}
continue
}
s, err := computeSignature(ctx, prox, modver)
if err != nil {
log.Printf("skipping %s: %v", modver, err)
} else {
fetch.ZipSignatures[s] = append(fetch.ZipSignatures[s], modver)
}
}
}
return writeGoFile(goFile)
}
func checkSignatures(ctx context.Context, prox *proxy.Client, args []string) error {
for _, arg := range args {
mv, err := internal.ParseModver(arg)
if err != nil {
return err
}
sig, err := computeSignature(ctx, prox, mv)
if err != nil {
return err
}
matches := fetch.ZipSignatures[sig]
fmt.Printf("%s: signature %s matches %v\n", arg, sig, matches)
}
return nil
}
func computeSignature(ctx context.Context, prox *proxy.Client, mv internal.Modver) (string, error) {
start := time.Now()
zr, err := prox.Zip(ctx, mv.Path, mv.Version)
if err != nil {
return "", err
}
contentDir, err := fs.Sub(zr, mv.String())
if err != nil {
return "", err
}
sig, err := fetch.FSSignature(contentDir)
if err != nil {
return "", err
}
dur := time.Since(start)
if *verbose {
fmt.Printf("%-40s %s %.1fs\n", mv, sig, dur.Seconds())
}
return sig, nil
}
// writeGoFile writes ZipSignatures back to the generated file.
func writeGoFile(filename string) error {
// Convert the ZipSignatures map to a slice of key-value pairs.
type kv struct {
Signature string
Modvers []internal.Modver
key string
}
var kvs []kv
for sig, mvs := range fetch.ZipSignatures {
kvs = append(kvs, kv{sig, mvs, mvs[0].String()})
}
// Sort the slice so that the diffs will show only the changes. Otherwise
// random map iteration order will result in messy diffs even if no
// signatures were added.
sort.Slice(kvs, func(i, j int) bool { return kvs[i].key < kvs[j].key })
// Execute the template.
var buf bytes.Buffer
if err := tmpl.Execute(&buf, kvs); err != nil {
return err
}
// Run gofmt.
src, err := format.Source(buf.Bytes())
if err != nil {
return err
}
return ioutil.WriteFile(filename, src, 0644)
}
// Template for the generated source file.
// The map value is a []Modver because it's possible for two modules to have the same contents.
// For example, github.com/aws/aws-sdk-go v1.9.0 and v1.9.44 (perhaps due to a bad tag?).
var tmpl = template.Must(template.New("").Parse(`
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by gen_zip_signatures.go; DO NOT EDIT.
package fetch
import "golang.org/x/pkgsite/internal"
var ZipSignatures = map[string][]internal.Modver{
{{range .}}
"{{.Signature}}": []internal.Modver{
{{range .Modvers -}}
{Path: "{{.Path}}", Version: "{{.Version}}"},
{{- end}}
},
{{- end}}
}
`))