Skip to content

Commit

Permalink
modernise
Browse files Browse the repository at this point in the history
  • Loading branch information
jpillora committed Jul 25, 2022
1 parent a81aefb commit cc594fe
Show file tree
Hide file tree
Showing 14 changed files with 141 additions and 133 deletions.
7 changes: 7 additions & 0 deletions .github/goreleaser.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# test this file with
# goreleaser --skip-publish --rm-dist --config goreleaser.yml
builds:
- env:
- CGO_ENABLED=0
flags:
- -trimpath
ldflags:
- -s -w -X main.version={{.Version}}
goos:
- linux
- darwin
- windows
- openbsd
goarch:
- 386
- amd64
Expand Down
25 changes: 17 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,21 @@ jobs:
name: Release
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@master
- name: goreleaser
uses: docker://goreleaser/goreleaser:latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@v3
with:
args: release --config .github/goreleaser.yml
if: success()
go-version: '1.18'
check-latest: true
cache: true
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3
with:
distribution: goreleaser
version: latest
args: release --rm-dist --config .github/goreleaser.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
on: [push, pull_request]
name: Test
jobs:
test:
strategy:
matrix:
go-version: [1.18.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.18'
check-latest: true
cache: true
- name: Build # no tests yet
run: go build -v -o /dev/null
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module github.com/jpillora/installer

go 1.14
go 1.18

// +heroku goVersion go1.14
require github.com/jpillora/opts v1.1.2

require (
github.com/jpillora/opts v1.1.2
github.com/rakyll/statik v0.1.7
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.0.0 // indirect
github.com/posener/complete v1.2.2-0.20190308074557-af07aa5181b3 // indirect
)
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,3 @@ github.com/jpillora/opts v1.1.2 h1:54/W0/fvo1DexHbL2utsW4rDhr5RXgojsPD2TA5oEzI=
github.com/jpillora/opts v1.1.2/go.mod h1:7p7X/vlpKZmtaDFYKs956EujFqA6aCrOkcCaS6UBcR4=
github.com/posener/complete v1.2.2-0.20190308074557-af07aa5181b3 h1:GqpA1/5oN1NgsxoSA4RH0YWTaqvUlQNeOpHXD/JRbOQ=
github.com/posener/complete v1.2.2-0.20190308074557-af07aa5181b3/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E=
github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs=
github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
123 changes: 54 additions & 69 deletions handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,21 @@ import (
"sync"
"text/template"
"time"

"github.com/jpillora/installer/scripts"
)

const (
cacheTTL = time.Hour
)

var (
userRe = `(\/([\w\-]{1,128}))?`
repoRe = `([\w\-\_]{1,128})`
releaseRe = `(@([\w\-\.\_]{1,128}?))?`
moveRe = `(!*)`
pathRe = regexp.MustCompile(`^` + userRe + `\/` + repoRe + releaseRe + moveRe + `$`)

userRe = `(\/([\w\-]{1,128}))?`
repoRe = `([\w\-\_]{1,128})`
releaseRe = `(@([\w\-\.\_]{1,128}?))?`
moveRe = `(!*)`
pathRe = regexp.MustCompile(`^` + userRe + `\/` + repoRe + releaseRe + moveRe + `$`)
lettersRe = regexp.MustCompile(`[^A-Za-z0-9\ :]`)
isTermRe = regexp.MustCompile(`(?i)^(curl|wget)\/`)
isHomebrewRe = regexp.MustCompile(`(?i)^homebrew`)
errNotFound = errors.New("not found")
Expand All @@ -50,7 +52,7 @@ func (q query) cacheKey() string {
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

//Handler serves install scripts using Github releases
// Handler serves install scripts using Github releases
type Handler struct {
Config
cacheMut sync.Mutex
Expand All @@ -59,37 +61,50 @@ type Handler struct {

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
http.Redirect(w, r, "https://github.com/jpillora/installer", http.StatusMovedPermanently)
http.Redirect(w, r, "https:// github.com/jpillora/installer", http.StatusMovedPermanently)
return
}
//calculate reponse type
var isTerm, isHomebrew, isText bool
switch r.URL.Query().Get("type") {
case "script":
isTerm = true
case "homebrew":
isHomebrew = true
case "text":
isText = true
default:
// calculate reponse type
ext := ""
script := ""
qtype := r.URL.Query().Get("type")
if qtype == "" {
ua := r.Header.Get("User-Agent")
switch {
case isTermRe.MatchString(ua):
isTerm = true
qtype = "script"
case isHomebrewRe.MatchString(ua):
isHomebrew = true
qtype = "ruby"
default:
isText = true
qtype = "text"
}
}
//type specific error response
// type specific error response
showError := func(msg string, code int) {
if isTerm {
msg = fmt.Sprintf("echo '%s'\n", msg)
cleaned := lettersRe.ReplaceAllString(msg, "")
if qtype == "text" {
cleaned = fmt.Sprintf("echo '%s'", cleaned)
}
http.Error(w, msg, http.StatusInternalServerError)
http.Error(w, cleaned, http.StatusInternalServerError)
}
switch qtype {
case "script":
w.Header().Set("Content-Type", "text/x-shellscript")
ext = "sh"
script = string(scripts.Shell)
case "homebrew", "ruby":
w.Header().Set("Content-Type", "text/ruby")
ext = "rb"
script = string(scripts.Homebrew)
case "text":
w.Header().Set("Content-Type", "text/plain")
ext = "txt"
script = string(scripts.Text)
default:
showError("Unknown type", http.StatusInternalServerError)
return
}
//"route"
// "route"
m := pathRe.FindStringSubmatch(r.URL.Path)
if len(m) == 0 {
showError("Invalid path", http.StatusBadRequest)
Expand All @@ -105,63 +120,36 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Google: false,
Insecure: r.URL.Query().Get("insecure") == "1",
}
//pick a user
// pick a user
if q.User == "" {
if q.Program == "micro" {
//micro > nano!
// micro > nano!
q.User = "zyedidia"
} else {
//use default user, but fallback to google
// use default user, but fallback to google
q.User = h.Config.User
q.Google = true
}
}
//fetch assets
// fetch assets
if err := h.getAssets(q); err != nil {
showError(err.Error(), http.StatusBadGateway)
return
}
//ready!
ext := ""
if isTerm {
w.Header().Set("Content-Type", "text/x-shellscript")
ext = "sh"
} else if isHomebrew {
w.Header().Set("Content-Type", "text/ruby")
ext = "rb"
} else {
if isText {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "repository: https://github.com/%s/%s\n", q.User, q.Program)
fmt.Fprintf(w, "user: %s\n", q.User)
fmt.Fprintf(w, "program: %s\n", q.Program)
fmt.Fprintf(w, "release: %s\n", q.Release)
fmt.Fprintf(w, "release assets:\n")
for i, a := range q.Assets {
fmt.Fprintf(w, " [#%02d] %s\n", i+1, a.URL)
}
fmt.Fprintf(w, "move-into-path: %v\n", q.MoveToPath)
fmt.Fprintf(w, "\nto see shell script, visit:\n %s%s?type=script\n", r.Host, r.URL.String())
fmt.Fprintf(w, "\nfor more information on this server, visit:\n github.com/jpillora/installer\n")
return
}
showError("Unknown type", http.StatusInternalServerError)
return
}
t, err := template.New("installer").Parse(string(installScript))
// load template
t, err := template.New("installer").Parse(script)
if err != nil {
http.Error(w, "Installer script invalid", http.StatusInternalServerError)
return
}
//execute script
// execute template
buff := bytes.Buffer{}
if err := t.Execute(&buff, q); err != nil {
showError("Template error: "+err.Error(), http.StatusInternalServerError)
return
}
log.Printf("serving script %s/%s@%s (%s)", q.User, q.Program, q.Release, ext)
//ready
// ready
w.Write(buff.Bytes())
}

Expand All @@ -170,10 +158,6 @@ type asset struct {
Is32bit, IsMac bool
}

func (a asset) target() string {
return fmt.Sprintf("%s_%s", a.OS, a.Arch)
}

func (h *Handler) get(url string, v interface{}) error {
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Accept", "application/vnd.github.v3+json")
Expand All @@ -182,19 +166,20 @@ func (h *Handler) get(url string, v interface{}) error {
}
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("Request failed: %s: %s", url, err)
return fmt.Errorf("request failed: %s: %s", url, err)
}
defer resp.Body.Close()

if resp.StatusCode == 404 {
return errNotFound
} else if resp.StatusCode != 200 {
return fmt.Errorf("%w: url %s", errNotFound, url)
}
if resp.StatusCode != 200 {
b, _ := ioutil.ReadAll(resp.Body)
return errors.New(http.StatusText(resp.StatusCode) + " " + string(b))
}

if err := json.NewDecoder(resp.Body).Decode(v); err != nil {
return fmt.Errorf("Download failed: %s: %s", url, err)
return fmt.Errorf("download failed: %s: %s", url, err)
}

return nil
Expand Down
13 changes: 7 additions & 6 deletions handler/handler_github.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func (h *Handler) getAssets(q *query) error {
}
cq, ok := h.cache[key]
h.cacheMut.Unlock()
if ok && time.Now().Sub(cq.Timestamp) < cacheTTL {
if ok && time.Since(cq.Timestamp) < cacheTTL {
//cache hit
*q = *cq
return nil
Expand All @@ -26,13 +26,14 @@ func (h *Handler) getAssets(q *query) error {
if err == nil {
//didn't need google
q.Google = false
} else if err == errNotFound && q.Google {
} else if errors.Is(err, errNotFound) && q.Google {
//use google to auto-detect user...
user, program, gerr := searchGoogle(q.Program)
if gerr != nil {
log.Printf("google search failed: %s", gerr)
} else {
if program == q.Program {
log.Printf("google search found: %s/%s", user, program)
if program != q.Program {
log.Printf("program mismatch: got %s: expected %s", q.Program, program)
}
q.Program = program
Expand Down Expand Up @@ -88,11 +89,11 @@ func (h *Handler) getAssetsNoCache(q *query) error {
}
}
if !found {
return fmt.Errorf("Release tag '%s' not found", release)
return fmt.Errorf("release tag '%s' not found", release)
}
}
if len(ghas) == 0 {
return errors.New("No assets found")
return errors.New("no assets found")
}
assets := []asset{}

Expand Down Expand Up @@ -140,7 +141,7 @@ func (h *Handler) getAssetsNoCache(q *query) error {
})
}
if len(assets) == 0 {
return errors.New("No downloads found for this release")
return errors.New("no downloads found for this release")
}
//TODO: handle duplicate asset.targets
q.Assets = assets
Expand Down
2 changes: 1 addition & 1 deletion handler/handler_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
var searchGithubRe = regexp.MustCompile(`https:\/\/github\.com\/(\w+)\/(\w+)`)

//uses im feeling lucky and grabs the "Location"
//header from the 302, which contains the IMDB ID
//header from the 302, which contains the github repo
func searchGoogle(phrase string) (user, project string, err error) {
phrase += " site:github.com"
log.Printf("google search for '%s'", phrase)
Expand Down
26 changes: 0 additions & 26 deletions handler/init.go

This file was deleted.

Loading

0 comments on commit cc594fe

Please sign in to comment.