Use GitHub API to setup Go vanity redirects

Signed-off-by: Steffen Vogel <post@steffenvogel.de>
This commit is contained in:
Steffen Vogel
2023-10-13 20:26:42 +02:00
parent 0ec1e60e13
commit f44aafa7cd
6 changed files with 181 additions and 50 deletions

View File

@@ -7,7 +7,7 @@ Files: website/static/CNAME pkg/selfupdate/keys/09BE3BAE8D55D4CD8579285A9675EAC3
Copyright: 2023 Steffen Vogel <post@steffenvogel.de>
License: CC0-1.0
Files: go.sum website/package.json website/yarn.lock nix/flake.lock docs/usage/** .renovaterc.json *.drawio *.svg
Files: go.sum scripts/go.sum website/package.json website/yarn.lock nix/flake.lock docs/usage/** .renovaterc.json *.drawio *.svg
Copyright: 2023 Steffen Vogel <post@steffenvogel.de>
License: Apache-2.0

View File

@@ -80,7 +80,8 @@ docs: cunicu
./cunicu docs --with-frontmatter
redirects:
bash ./scripts/generate_vanity_redirects.sh
cd scripts && \
go run ./generate_vanity_redirects.go -static-dir ../website/static
completions: completions/cunicu.bash completions/cunicu.zsh completions/cunicu.fish

View File

@@ -0,0 +1,155 @@
// SPDX-FileCopyrightText: 2023 Steffen Vogel <post@steffenvogel.de>
// SPDX-License-Identifier: Apache-2.0
package main
import (
"context"
"flag"
"fmt"
"html/template"
"io"
"log"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/google/go-github/v56/github"
"golang.org/x/mod/modfile"
)
type tmplData struct {
Module string
Repo string
}
func generate(mod, repo, staticDir, prefix string) error {
tmpl, err := template.New("index").Parse(`<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="go-import" content="{{ .Module }} git {{ .Repo }}">
<meta http-equiv="Refresh" content="0; url={{ .Repo }}" />
</head>
<body>
</body>
</html>`)
if err != nil {
return err
}
subDir := strings.TrimPrefix(mod, prefix)
dir := filepath.Join(staticDir, subDir)
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
fn := filepath.Join(dir, "index.html")
f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644)
if err != nil {
return err
}
if err := tmpl.Execute(f, tmplData{
Module: mod,
Repo: repo,
}); err != nil {
return err
}
if _, err := f.WriteString(`
<!--
SPDX-FileCopyrightText: 2023 Steffen Vogel <post@steffenvogel.de>
SPDX-License-Identifier: Apache-2.0
-->
`); err != nil {
return err
}
log.Println("Added redirect", mod, repo)
return f.Close()
}
func getModsFromGitHub(ctx context.Context, owner string) (map[string]string, error) {
client := github.NewClient(nil)
if token := os.Getenv("GITHUB_TOKEN"); token != "" {
client = client.WithAuthToken(token)
}
repos, _, err := client.Repositories.List(context.Background(), owner, nil)
if err != nil {
return nil, fmt.Errorf("failed to list repos: %w", err)
}
mods := map[string]string{}
for _, repo := range repos {
file, _, err := client.Repositories.DownloadContents(ctx, owner, *repo.Name, "go.mod", nil)
if err != nil {
log.Printf("Failed to download %s/%s/go.mod: %v", owner, *repo.Name, err)
continue
}
defer file.Close()
contents, err := io.ReadAll(file)
if err != nil {
return nil, fmt.Errorf("failed to download go.mod: %w", err)
}
modFile, err := modfile.Parse("go.mod", contents, nil)
if err != nil {
return nil, fmt.Errorf("failed to parse go.mod: %w", err)
}
if modFile.Module == nil {
continue
}
mods[modFile.Module.Mod.Path] = *repo.CloneURL
}
return mods, nil
}
func getModBase(pkg string) string {
re := regexp.MustCompile(`\/v[0-9]`)
return re.ReplaceAllString(pkg, "")
}
func main() {
prefix := flag.String("prefix", "cunicu.li", "Prefix")
owner := flag.String("owner", "cunicu", "GitHub user/org")
staticDir := flag.String("static-dir", "./website/static", "Directory in which the generated files should be placed")
flag.Parse()
ctx := context.Background()
mods, err := getModsFromGitHub(ctx, *owner)
if err != nil {
log.Fatalf("Failed to list packages: %v", err)
}
for mod, repo := range mods {
modBase := getModBase(mod)
if !strings.HasPrefix(mod, *prefix) {
continue
}
if err := generate(mod, repo, *staticDir, *prefix); err != nil {
log.Fatalf("Failed to generate HTML file: %v", err)
}
if mod == modBase {
continue
}
if err := generate(modBase, repo, *staticDir, *prefix); err != nil {
log.Fatalf("Failed to generate HTML file: %v", err)
}
}
}

View File

@@ -1,48 +0,0 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2023 Steffen Vogel <post@steffenvogel.de>
# SPDX-License-Identifier: Apache-2.0
STATIC_DIR="./website/static"
PACKAGES=(
cunicu
hawkes
gont/v2
go-babel
go-openpgp-card
go-piv
go-pmtud
go-rosenpass
go-ykoath
)
function generate() {
mkdir -p "${STATIC_DIR}/${1}"
cat > "${STATIC_DIR}/${1}/index.html" <<EOF
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="go-import" content="cunicu.li/${1} git https://github.com/cunicu/${1/\/v[0-9]/}.git">
<meta http-equiv="Refresh" content="0; url=https://github.com/cunicu/${1/\/v[0-9]/}" />
</head>
<body>
</body>
<!--
SPDX-FileCopyrightText: 2023 Steffen Vogel <post@steffenvogel.de>
SPDX-License-Identifier: Apache-2.0
-->
</html>
EOF
}
for i in "${!PACKAGES[@]}"; do
PACKAGE=${PACKAGES[$i]}
PACKAGE_BASE=${PACKAGE/\/v[0-9]/}
generate ${PACKAGE}
if [ ${PACKAGE} != ${PACKAGE_BASE} ]; then
generate ${PACKAGE_BASE}
fi
done

13
scripts/go.mod Normal file
View File

@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2023 Steffen Vogel <post@steffenvogel.de>
// SPDX-License-Identifier: Apache-2.0
module cunicu.li/cunicu/scripts
go 1.21.0
require (
github.com/google/go-github/v56 v56.0.0
golang.org/x/mod v0.13.0
)
require github.com/google/go-querystring v1.1.0 // indirect

10
scripts/go.sum Normal file
View File

@@ -0,0 +1,10 @@
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v56 v56.0.0 h1:TysL7dMa/r7wsQi44BjqlwaHvwlFlqkK8CtBWCX3gb4=
github.com/google/go-github/v56 v56.0.0/go.mod h1:D8cdcX98YWJvi7TLo7zM4/h8ZTx6u6fwGEkCdisopo0=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=