Avatar (Fabio Alessandro Locati|Fale)'s blog

A lightweight approach to Go vanity import paths

September 23, 2018

Golang uses URLs for the dependencies packages resolution. To unbundle the code repository hosting the package and the import path, Golang supports the idea of Vanity Import Paths. The way this has been implemented is that, as long as the import path points to a page where Go can find the real package URL, it will follow through.

So, we will need to create a web server that can serve pages in a way that the Go toolchains can understand. To do so, I use the following code:

package main

import (

type conversion struct {
    Vanity string
    Real   string
    Domain string

var conversions = []conversion{
    conversion{Domain: "go.fale.io", Vanity: "test", Real: "https://github.com/fale/test"},

var tpl = template.Must(template.New("main").Parse(`<!DOCTYPE html>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <meta name="go-import" content="{{ .Domain }}/{{ .Vanity }} git {{ .Real }}">

func main() {

    wwwPort := "80"
    if os.Getenv("DEBUG") == "true" {
        wwwPort = "10080"

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s %s %s\n", r.Method, r.URL, r.Proto)
        for _, c := range conversions {
            if r.URL.Path[1:] == c.Vanity || strings.HasPrefix(r.URL.Path[1:], c.Vanity+"/") {
                tpl.Execute(w, c)
        http.Error(w, "Not found", 404)

    http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok")) })
    http.HandleFunc("/readiness", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok")) })

    log.Println("Ready to serve")
    http.ListenAndServe(":"+wwwPort, nil)

For every repository we have, we need to add one more item in the conversions array.