about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tools/govanity/.drone.yml18
-rw-r--r--tools/govanity/.envrc1
-rw-r--r--tools/govanity/.gitignore1
-rw-r--r--tools/govanity/Dockerfile30
-rw-r--r--tools/govanity/README.org15
-rw-r--r--tools/govanity/flake.lock90
-rw-r--r--tools/govanity/flake.nix37
-rw-r--r--tools/govanity/fly.toml34
-rw-r--r--tools/govanity/go.mod5
-rw-r--r--tools/govanity/go.sum3
-rw-r--r--tools/govanity/main.go121
-rwxr-xr-xtools/govanity/scripts/deploy.sh14
-rw-r--r--tools/govanity/templates/index.html.tpl14
-rw-r--r--tools/govanity/templates/module.html.tpl12
-rw-r--r--tools/govanity/vanity.yaml9
15 files changed, 404 insertions, 0 deletions
diff --git a/tools/govanity/.drone.yml b/tools/govanity/.drone.yml
new file mode 100644
index 0000000..ee50017
--- /dev/null
+++ b/tools/govanity/.drone.yml
@@ -0,0 +1,18 @@
+---
+kind: pipeline
+type: exec
+name: default
+
+trigger:
+  event:
+    - push
+  branch:
+    - main
+
+steps:
+  - name: deploy
+    environment:
+      FLY_API_TOKEN:
+        from_secret: FLY_API_TOKEN
+    commands:
+      - nix run .#deploy
diff --git a/tools/govanity/.envrc b/tools/govanity/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/tools/govanity/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/tools/govanity/.gitignore b/tools/govanity/.gitignore
new file mode 100644
index 0000000..c4a847d
--- /dev/null
+++ b/tools/govanity/.gitignore
@@ -0,0 +1 @@
+/result
diff --git a/tools/govanity/Dockerfile b/tools/govanity/Dockerfile
new file mode 100644
index 0000000..20df29f
--- /dev/null
+++ b/tools/govanity/Dockerfile
@@ -0,0 +1,30 @@
+FROM golang:1.16 AS builder
+
+ENV USER=app
+RUN adduser \
+    --disabled-password \
+    --gecos "" \
+    --home "/src" \
+    --shell "/sbin/nologin" \
+    --uid "10001" \
+    "${USER}"
+
+WORKDIR /src
+
+ADD go.mod /src
+ADD go.sum /src
+RUN go mod download
+
+ADD . /src
+
+RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -a -installsuffix cgo -ldflags '-extldflags "-static"' -o app .
+
+FROM scratch
+COPY --from=builder /src/app /vanity
+COPY --from=builder /src/vanity.yaml /vanity.yaml
+COPY --from=builder /etc/passwd /etc/passwd
+COPY --from=builder /etc/group /etc/group
+
+USER app:app
+
+ENTRYPOINT ["/vanity"]
diff --git a/tools/govanity/README.org b/tools/govanity/README.org
new file mode 100644
index 0000000..7955ead
--- /dev/null
+++ b/tools/govanity/README.org
@@ -0,0 +1,15 @@
+A service to manage vanity URLs for go packages.
+
+It makes it possible to install tools like this:
+#+begin_src sh
+GOPRIVATE=1 go install -v golang.fcuny.net/tools/cmd/music-organizer@latest
+#+end_src
+
+* Build
+Running =nix build= in the repository will create the binary.
+* Run
+Running =nix run= in the repository will start the server.
+* Deployment
+To update the application with the most recent code, run =make deploy=.
+* Configuration
+Add repositories to the [[file+sys:vanity.yaml][configuration file]].
diff --git a/tools/govanity/flake.lock b/tools/govanity/flake.lock
new file mode 100644
index 0000000..a334e5c
--- /dev/null
+++ b/tools/govanity/flake.lock
@@ -0,0 +1,90 @@
+{
+  "nodes": {
+    "flake-utils": {
+      "locked": {
+        "lastModified": 1644229661,
+        "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1652298859,
+        "narHash": "sha256-hcwRboK+NxMWUJh0fQ3VsocDcAHrYJ95FmPEHlddV0Y=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "051448e41537c3463ae776d46115d01afb6c498d",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "release-21.11",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_2": {
+      "locked": {
+        "lastModified": 1645655918,
+        "narHash": "sha256-ZfbEFRW7o237+A1P7eTKhXje435FCAoe0blj2n20Was=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "77a7a4197740213879b9a1d2e1788c6c8ade4274",
+        "type": "github"
+      },
+      "original": {
+        "id": "nixpkgs",
+        "type": "indirect"
+      }
+    },
+    "pre-commit-hooks": {
+      "inputs": {
+        "flake-utils": "flake-utils",
+        "nixpkgs": "nixpkgs_2"
+      },
+      "locked": {
+        "lastModified": 1649054408,
+        "narHash": "sha256-wz8AH7orqUE4Xog29WMTqOYBs0DMj2wFM8ulrTRVgz0=",
+        "path": "/nix/store/2bw70fnmml6w0vjkcj2n7jvzcxpgprrm-source",
+        "rev": "e5e7b3b542e7f4f96967966a943d7e1c07558042",
+        "type": "path"
+      },
+      "original": {
+        "id": "pre-commit-hooks",
+        "type": "indirect"
+      }
+    },
+    "root": {
+      "inputs": {
+        "nixpkgs": "nixpkgs",
+        "pre-commit-hooks": "pre-commit-hooks",
+        "utils": "utils"
+      }
+    },
+    "utils": {
+      "locked": {
+        "lastModified": 1649676176,
+        "narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "a4b154ebbdc88c8498a5c7b01589addc9e9cb678",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/tools/govanity/flake.nix b/tools/govanity/flake.nix
new file mode 100644
index 0000000..d2c2254
--- /dev/null
+++ b/tools/govanity/flake.nix
@@ -0,0 +1,37 @@
+{
+  description = "govanity";
+
+  inputs = {
+    nixpkgs.url = "github:nixos/nixpkgs/release-21.11";
+    utils.url = "github:numtide/flake-utils";
+  };
+
+  outputs = { self, utils, nixpkgs, pre-commit-hooks }:
+    utils.lib.eachDefaultSystem (system:
+      let pkgs = nixpkgs.legacyPackages.${system};
+      in {
+
+        defaultPackage = self.packages.${system}.vanity;
+
+        packages.vanity = pkgs.buildGoModule {
+          pname = "vanity";
+          version = "0.0.1";
+          src = ./.;
+          vendorSha256 = "sha256-iu2QE+vvenFWpOOz1NHVQHudiWkvkKqZvD4ZX4Xa1sY=";
+          nativeBuildInputs = with pkgs; [ go ];
+        };
+
+        apps = {
+          deploy = pkgs.pkgs.writeShellScriptBin "run-deploy" ''
+            set -euxo pipefail
+            export PATH=${
+              pkgs.lib.makeBinPath [ pkgs.go pkgs.git pkgs.jq pkgs.flyctl ]
+            }:$PATH
+            bash ./scripts/deploy.sh
+          '';
+        };
+
+        devShell = with pkgs;
+          mkShell { nativeBuildInputs = [ git go gopls golangci-lint bash ]; };
+      });
+}
diff --git a/tools/govanity/fly.toml b/tools/govanity/fly.toml
new file mode 100644
index 0000000..286cd1e
--- /dev/null
+++ b/tools/govanity/fly.toml
@@ -0,0 +1,34 @@
+app = "golang-fcuny-net"
+
+kill_signal = "SIGINT"
+kill_timeout = 5
+
+[env]
+
+[experimental]
+  allowed_public_ports = []
+  auto_rollback = true
+
+[[services]]
+  internal_port = 8080
+  protocol = "tcp"
+  script_checks = []
+
+  [services.concurrency]
+    hard_limit = 25
+    soft_limit = 20
+    type = "connections"
+
+  [[services.ports]]
+    handlers = ["http"]
+    port = 80
+
+  [[services.ports]]
+    handlers = ["tls", "http"]
+    port = 443
+
+  [[services.tcp_checks]]
+    grace_period = "1s"
+    interval = "15s"
+    restart_limit = 6
+    timeout = "2s"
diff --git a/tools/govanity/go.mod b/tools/govanity/go.mod
new file mode 100644
index 0000000..d37f146
--- /dev/null
+++ b/tools/govanity/go.mod
@@ -0,0 +1,5 @@
+module golang.fcuny.net/vanity
+
+go 1.16
+
+require gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
diff --git a/tools/govanity/go.sum b/tools/govanity/go.sum
new file mode 100644
index 0000000..97f8991
--- /dev/null
+++ b/tools/govanity/go.sum
@@ -0,0 +1,3 @@
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/tools/govanity/main.go b/tools/govanity/main.go
new file mode 100644
index 0000000..dd6d653
--- /dev/null
+++ b/tools/govanity/main.go
@@ -0,0 +1,121 @@
+package main
+
+import (
+	"bytes"
+	"embed"
+	"flag"
+	"html/template"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"strings"
+
+	"gopkg.in/yaml.v3"
+)
+
+//go:embed templates
+var tpls embed.FS
+
+type repository struct {
+	Name string `yaml:"name"`
+	Repo string `yaml:"repo"`
+}
+
+type config struct {
+	BaseUrl      string       `yaml:"baseUrl"`
+	VCS          string       `yaml:"vcs"`
+	Repositories []repository `yaml:"repositories"`
+}
+
+type moduleTmpl struct {
+	Name    string
+	Repo    string
+	VCS     string
+	BaseUrl string
+}
+
+func main() {
+	flag.Parse()
+	buf, err := ioutil.ReadFile("vanity.yaml")
+	if err != nil {
+		log.Fatalf("failed to read the configuration: %+v", err)
+	}
+
+	cfg := &config{}
+	err = yaml.Unmarshal(buf, cfg)
+	if err != nil {
+		log.Fatalf("failed to parse the YAML configuration: %+v", err)
+	}
+
+	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(200)
+		w.Write([]byte("ok"))
+	})
+
+	http.HandleFunc("/", goGet(cfg))
+
+	log.Printf("starting web server on :8080")
+	log.Fatal(http.ListenAndServe(":8080", nil))
+}
+
+func goGet(cfg *config) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		if r.Method != http.MethodGet {
+			status := http.StatusMethodNotAllowed
+			http.Error(w, http.StatusText(status), status)
+			return
+		}
+
+		if r.FormValue("go-get") == "1" {
+			pathParts := strings.Split(r.URL.Path, "/")
+			for _, m := range cfg.Repositories {
+				if pathParts[1] == m.Name {
+					goGetModule(w, r, m, cfg)
+					return
+				}
+			}
+			status := http.StatusNotFound
+			http.Error(w, http.StatusText(status), status)
+			return
+		}
+		browserURL(w, r, cfg)
+	}
+}
+
+func goGetModule(w http.ResponseWriter, r *http.Request, m repository, cfg *config) {
+	tmpl, err := template.ParseFS(tpls, "templates/module.html.tpl")
+	if err != nil {
+		log.Fatal(err)
+	}
+	mod := moduleTmpl{
+		VCS:     cfg.VCS,
+		BaseUrl: cfg.BaseUrl,
+		Name:    m.Name,
+		Repo:    m.Repo,
+	}
+	var buf bytes.Buffer
+	if err := tmpl.Execute(&buf, mod); err != nil {
+		log.Printf("error: %+v", err)
+		status := http.StatusInternalServerError
+		http.Error(w, http.StatusText(status), status)
+	} else {
+		w.Header().Set("Cache-Control", "no-store")
+		w.Write(buf.Bytes())
+	}
+}
+
+func browserURL(w http.ResponseWriter, r *http.Request, cfg *config) {
+	tmpl, err := template.ParseFS(tpls, "templates/index.html.tpl")
+	if err != nil {
+		log.Fatal(err)
+	}
+	var buf bytes.Buffer
+	if err := tmpl.Execute(&buf, cfg); err != nil {
+		log.Printf("error: %+v", err)
+		status := http.StatusInternalServerError
+		http.Error(w, http.StatusText(status), status)
+	} else {
+		w.Header().Set("Cache-Control", "no-store")
+		w.Write(buf.Bytes())
+	}
+}
diff --git a/tools/govanity/scripts/deploy.sh b/tools/govanity/scripts/deploy.sh
new file mode 100755
index 0000000..4f46b68
--- /dev/null
+++ b/tools/govanity/scripts/deploy.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+git diff --exit-code
+git diff --staged --exit-code
+
+flyctl deploy
+
+VERSION=$(flyctl info -j |jq -r '.App | "\(.Name)/v\(.Version)"')
+
+git tag -a --message ${VERSION} ${VERSION}
+git push origin --all
+git push origin --tags
+
+flyctl agent stop
diff --git a/tools/govanity/templates/index.html.tpl b/tools/govanity/templates/index.html.tpl
new file mode 100644
index 0000000..fd8fc56
--- /dev/null
+++ b/tools/govanity/templates/index.html.tpl
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>golang repo</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+  </head>
+  <body>
+    <ul>
+      {{ range $idx, $m := .Repositories }}
+      <li>go get <a href="{{ $m.Repo }}">{{ $.BaseUrl }}/{{ $m.Name }}/</a></li>
+      {{end}}
+    </ul>
+  </body>
+</html>
diff --git a/tools/govanity/templates/module.html.tpl b/tools/govanity/templates/module.html.tpl
new file mode 100644
index 0000000..fab3414
--- /dev/null
+++ b/tools/govanity/templates/module.html.tpl
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>{{.Name}}: golang repo</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+    <meta name="go-import" content="{{.BaseUrl}}/{{.Name}} {{.VCS}} {{.Repo}}">
+    <meta name="go-source" content="{{.BaseUrl}}/{{.Name}} _ {{.Repo}}/src{/dir} {{.Repo}}/src{/dir}/{file}/#L{line}">
+  </head>
+  <body>
+    go get {{.BaseUrl}}/{{.Name}}
+  </body>
+</html>
diff --git a/tools/govanity/vanity.yaml b/tools/govanity/vanity.yaml
new file mode 100644
index 0000000..cfe5b03
--- /dev/null
+++ b/tools/govanity/vanity.yaml
@@ -0,0 +1,9 @@
+baseUrl: golang.fcuny.net
+vcs: git
+repositories:
+  - name: vanity
+    repo: https://git.fcuny.net/fcuny/govanity
+  - name: tools
+    repo: https://git.fcuny.net/fcuny/tools
+  - name: mpd-stats
+    repo: https://git.fcuny.net/fcuny/mpd-stats