зеркало из https://github.com/docker/get-involved.git
186 строки
5.0 KiB
Go
186 строки
5.0 KiB
Go
/*
|
|
Copyright 2020 The Docker Authors
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"html/template"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/google/go-github/v32/github"
|
|
)
|
|
|
|
var (
|
|
DockerOrgs = []string{"Docker", "docker-sandbox"}
|
|
allowedRepoRe = regexp.MustCompile("^[a-z][-a-z0-9]+$")
|
|
)
|
|
|
|
// repoInfo provides a simple holder for GitHub repo information needed to
|
|
// generate go mod redirects.
|
|
type repoInfo struct {
|
|
// Org is the name of the github organization the repo is in.
|
|
Org string
|
|
// Repo is the name of the github repo within the organizatiøon (e.g.
|
|
// "serving", NOT "docker/serving")
|
|
Repo string
|
|
// DefaultBranch is the name of the default branch. This will be changing
|
|
// from "master" to "main" over time.
|
|
DefaultBranch string
|
|
}
|
|
|
|
type riSlice []repoInfo
|
|
|
|
func main() {
|
|
repos, err := fetchRepos(dockerOrgs)
|
|
if err != nil {
|
|
log.Fatal("Failed to fetch repos: ", err)
|
|
}
|
|
for _, repo := range repos {
|
|
if createGoGetFile(repo) != nil {
|
|
log.Fatalf("Unable to create go mod file for %s: %v", repo, err)
|
|
}
|
|
}
|
|
if appendRedirs(repos) != nil {
|
|
log.Fatal("Failed to write redir file: ", err)
|
|
}
|
|
}
|
|
|
|
func fetchRepos(orgs []string) ([]repoInfo, error) {
|
|
ctx := context.Background()
|
|
allRepos := riSlice{}
|
|
client := github.NewClient(nil)
|
|
for _, org := range orgs {
|
|
opts := &github.RepositoryListByOrgOptions{
|
|
ListOptions: github.ListOptions{PerPage: 50},
|
|
}
|
|
for {
|
|
repos, resp, err := client.Repositories.ListByOrg(ctx, org, opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, r := range repos {
|
|
if !allowedRepoRe.MatchString(*r.Name) {
|
|
log.Printf("Ignoring repo %s, matched by ignore %q", *r.Name, allowedRepoRe)
|
|
continue
|
|
}
|
|
if *r.Archived {
|
|
log.Print("Ignoring archived repo: ", *r.Name)
|
|
continue
|
|
}
|
|
allRepos = append(allRepos, repoInfo{org, *r.Name, *r.DefaultBranch})
|
|
}
|
|
if resp.NextPage == 0 {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
sort.Sort(allRepos)
|
|
return allRepos, nil
|
|
}
|
|
|
|
const (
|
|
goHTML = `<html><head>
|
|
<meta name="go-import" content="docker.dev/{{.Repo}} git https://github.com/{{.Org}}/{{.Repo}}">
|
|
<meta name="go-source" content="docker.dev/{{.Repo}} https://github.com/{{.Org}}/{{.Repo}} https://github.com/{{.Org}}/{{.Repo}}/tree/{{.DefaultBranch}}{/dir} https://github.com/{{.Org}}/{{.Repo}}/blob/{{.DefaultBranch}}{/dir}/{file}#L{line}">
|
|
</head></html>
|
|
`
|
|
redirText = `/{{.Repo}}/* go-get=1 /golang/{{.Repo}}.html 200
|
|
`
|
|
|
|
autogenPrefix = `
|
|
# The following lines are AUTO-GENERATED
|
|
#
|
|
# DO NOT EDIT!
|
|
#
|
|
# To regenerate, run:
|
|
`
|
|
)
|
|
|
|
func appendRedirs(ris []repoInfo) error {
|
|
|
|
redirFilename := "static/_redirects"
|
|
redirFile, err := os.OpenFile(redirFilename, os.O_RDWR|os.O_CREATE, 0755)
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to open %q: %w", redirFilename, err)
|
|
}
|
|
defer redirFile.Close()
|
|
|
|
redirs, err := ioutil.ReadAll(redirFile)
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to read %q: %w", redirFilename, err)
|
|
}
|
|
|
|
seekTo := int64(strings.Index(string(redirs), autogenPrefix))
|
|
if seekTo == -1 {
|
|
seekTo = int64(len(redirs))
|
|
}
|
|
if _, err := redirFile.Seek(seekTo, 0); err != nil {
|
|
return fmt.Errorf("Unable to seek in %q: %w", redirFilename, err)
|
|
}
|
|
if redirFile.Truncate(seekTo) != nil {
|
|
return fmt.Errorf("Unable to truncate %q: %w", redirFilename, err)
|
|
}
|
|
|
|
if _, err := redirFile.WriteString(autogenPrefix); err != nil {
|
|
return fmt.Errorf("Unable to write to %q: %w", redirFilename, err)
|
|
}
|
|
if _, err := redirFile.WriteString(fmt.Sprintf("# %s\n", strings.Join(os.Args, " "))); err != nil {
|
|
return fmt.Errorf("Unable to write to %q: %w", redirFilename, err)
|
|
}
|
|
for _, ri := range ris {
|
|
if redirTemplate.Execute(redirFile, ri) != nil {
|
|
return fmt.Errorf("Unable to write %s to %q: %w", ri.Repo, redirFilename, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var fileTemplate = template.Must(template.New("gohtml").Parse(goHTML))
|
|
var redirTemplate = template.Must(template.New("redir").Parse(redirText))
|
|
|
|
// createGoGetFile creates a static HTML file providing a docker.dev mapping
|
|
// for the specified org and repo.
|
|
func createGoGetFile(ri repoInfo) error {
|
|
filename := fmt.Sprintf("static/golang/%s.html", ri.Repo)
|
|
file, err := os.Create(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
return fileTemplate.Execute(file, ri)
|
|
}
|
|
|
|
func (ri repoInfo) String() string {
|
|
return ri.Org + "/" + ri.Repo
|
|
}
|
|
|
|
func (ris riSlice) Len() int {
|
|
return len(ris)
|
|
}
|
|
|
|
func (ris riSlice) Less(i, j int) bool {
|
|
return ris[i].Repo < ris[j].Repo
|
|
}
|
|
|
|
func (ris riSlice) Swap(i, j int) {
|
|
ris[i], ris[j] = ris[j], ris[i]
|
|
|
|
}
|