diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..be637d3 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,62 @@ +name: Go +on: + push: + branches: [ main ] + paths: + - '**.go' + - 'go.mod' + - '.golangci.yml' + - '.github/workflows/go.yml' + pull_request: + paths: + - '**.go' + - 'go.mod' + - '.golangci.yml' + - '.github/workflows/go.yml' +env: + GOPROXY: "https://proxy.golang.org" + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + - name: Install Go + uses: actions/setup-go@v6 + with: + go-version: 1.25.x + - name: Check Go module tidiness + shell: bash + run: | + go mod tidy + STATUS=$(git status --porcelain) + if [ ! -z "$STATUS" ]; then + echo "Unstaged files:" + echo $STATUS + echo "Run 'go mod tidy' commit them" + exit 1 + fi + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v7 + with: + version: latest + args: --timeout=30m + + test: + name: Test + strategy: + matrix: + go-version: [ 1.25.x ] + platform: [ ubuntu-latest ] + runs-on: ${{ matrix.platform }} + steps: + - name: Checkout code + uses: actions/checkout@v6 + - name: Install Go + uses: actions/setup-go@v6 + with: + go-version: ${{ matrix.go-version }} + - name: Run tests with coverage + run: go test -shuffle=on -v -race ./... diff --git a/.github/workflows/lsif.yml b/.github/workflows/lsif.yml deleted file mode 100644 index 49ecaa7..0000000 --- a/.github/workflows/lsif.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: LSIF -on: - push: - paths: - - '**.go' - - 'go.mod' - - '.github/workflows/lsif.yml' -env: - GOPROXY: "https://proxy.golang.org" - -jobs: - lsif-go: - if: github.repository == 'unknwon/go-import-server' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Generate LSIF data - uses: sourcegraph/lsif-go-action@master - - name: Upload LSIF data to sourcegraph.com - continue-on-error: true - uses: docker://sourcegraph/src-cli:latest - with: - args: lsif upload -github-token=${{ secrets.GITHUB_TOKEN }} diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..fabbdb6 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,36 @@ +version: "2" +linters: + enable: + - nakedret + - rowserrcheck + - unconvert + - unparam + settings: + govet: + disable: + # printf: non-constant format string in call to fmt.Errorf (govet) + # showing up since golangci-lint version 1.60.1 + - printf + nakedret: + max-func-lines: 0 # Disallow any unnamed return statement + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/README.md b/README.md index f1a6398..181fb60 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ HTTP server for canonical "go get" import path. It supports all versions of `go Install from source or download binaries on [GitHub Releases](https://github.com/unknwon/go-import-server/releases). -The minimum requirement of Go is **1.16**, and 64-bit system is required because of [a bug in BadgerDB](https://github.com/dgraph-io/badger/issues/953). +The minimum requirement of Go is **1.25**. ```sh -$ go get unknwon.dev/go-import-server +$ go install unknwon.dev/go-import-server@latest ``` ### Configuration @@ -18,7 +18,7 @@ Example for this tool itself (save as `app.toml`): ```toml addr = "127.0.0.1:4333" -db_path = "app.db" +stats_path = "stats.json" [[packages]] import_path = "unknwon.dev/go-import-server" @@ -27,6 +27,9 @@ repo = "https://github.com/unknwon/go-import-server" branch = "main" ``` +>[!warning] +> Starting version 0.5.0, the `db_path` is removed and statistics are stored in a JSON file. Historical data will not be migrated automatically. + Assuming `$GOPATH/bin` has been added to your `$PATH` environment variable. ```sh @@ -70,7 +73,7 @@ auth_username = "superuser" auth_password = "supersecure" ``` -The [BadgerDB](https://github.com/dgraph-io/badger) is used to store total page views and number of `go get`s. +A JSON file is used to store total page views and number of `go get`s. Here is an example dump: diff --git a/go.mod b/go.mod index 611c1f8..f3c0a69 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,28 @@ module unknwon.dev/go-import-server -go 1.16 +go 1.25.0 require ( github.com/BurntSushi/toml v0.3.1 - github.com/dgraph-io/badger/v2 v2.2007.4 github.com/flamego/auth v0.0.0-20210831041357-158bdcabc1f1 github.com/flamego/flamego v1.0.1 github.com/flamego/template v1.0.0 github.com/prometheus/client_golang v1.2.1 unknwon.dev/clog/v2 v2.0.0 ) + +require ( + github.com/alecthomas/participle/v2 v2.0.0-alpha7 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.0 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/golang/protobuf v1.3.2 // indirect + github.com/mattn/go-colorable v0.1.9 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect + github.com/prometheus/common v0.7.0 // indirect + github.com/prometheus/procfs v0.0.5 // indirect + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect +) diff --git a/go.sum b/go.sum index 8daf657..a9157de 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/participle/v2 v2.0.0-alpha5/go.mod h1:Z1zPLDbcGsVsBYsThKXY00i84575bN/nMczzIrU4rWU= github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= github.com/alecthomas/participle/v2 v2.0.0-alpha7/go.mod h1:NumScqsC42o9x+dGj8/YqsIfhrIQjFEOFovxotbBirA= @@ -11,30 +9,15 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA= github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= -github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= -github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= -github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= @@ -47,7 +30,6 @@ github.com/flamego/flamego v1.0.1 h1:rHcvSFcFHfoAEZUQoqXVyVig/xbsjF0/hm7Fo4oZCBo github.com/flamego/flamego v1.0.1/go.mod h1:sMqWT2ONQkZsCHte/k8hYmfnbbLgnfv7lL+VJpfv+EU= github.com/flamego/template v1.0.0 h1:geUpBq+j0L2Wp+AuAQ7QxpZedRwG/EeYFZJ4llkPmA4= github.com/flamego/template v1.0.0/go.mod h1:ZS9Li2amrupO/3sXS1LRNrAqAcxJI8lG1hhlDBZ4R2s= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -58,24 +40,14 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= -github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= @@ -87,14 +59,11 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -116,18 +85,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -135,24 +94,17 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -162,7 +114,6 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/main.go b/main.go index b7afb59..386f9d6 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,6 @@ import ( "os/signal" "github.com/BurntSushi/toml" - "github.com/dgraph-io/badger/v2" "github.com/flamego/auth" "github.com/flamego/flamego" "github.com/flamego/template" @@ -35,11 +34,10 @@ func main() { log.Fatal("Failed to load config: %v", err) } - db, stats, err := getDBWithStats(config.DBPath) + stats, err := getStatsFromJSON(config.StatsPath) if err != nil { - log.Fatal("Failed to get database with stats: %v", err) + log.Fatal("Failed to load stats: %v", err) } - defer func() { _ = db.Close() }() fs, err := template.EmbedFS(templates, "templates", []string{".tmpl"}) if err != nil { @@ -102,7 +100,7 @@ func main() { setupPrometheusMetrics(stats) done := make(chan struct{}) - go stats.start(db, done) + go stats.start(config.StatsPath, done) s := newServer(config.Addr, f) log.Info("Listening on http://%s...", s.Addr) @@ -119,9 +117,9 @@ func main() { } type config struct { - Addr string - DBPath string `toml:"db_path"` - Packages []struct { + Addr string + StatsPath string `toml:"stats_path"` + Packages []struct { ImportPath string `toml:"import_path"` Subpath string Repo string @@ -142,23 +140,16 @@ func loadConfig(path string) (*config, error) { return &c, nil } -func getDBWithStats(path string) (*badger.DB, *stats, error) { - opts := badger.DefaultOptions(path) - db, err := badger.Open(opts) - if err != nil { - return nil, nil, fmt.Errorf("open: %v", err) - } - - // Retrieve current stats in database. +func getStatsFromJSON(path string) (*stats, error) { s := &stats{ pkgsView: make(map[string]*int64), pkgsGet: make(map[string]*int64), } - if err = s.loadFromDB(db); err != nil { - return nil, nil, fmt.Errorf("load stats from DB: %v", err) + if err := s.loadFromJSON(path); err != nil { + return nil, fmt.Errorf("load stats from JSON: %v", err) } - return db, s, nil + return s, nil } func newServer(addr string, f *flamego.Flame) *http.Server { @@ -167,7 +158,7 @@ func newServer(addr string, f *flamego.Flame) *http.Server { Handler: f, } - quit := make(chan os.Signal) + quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt) go func() { <-quit diff --git a/stats.go b/stats.go index 30a2590..425e112 100644 --- a/stats.go +++ b/stats.go @@ -1,12 +1,11 @@ package main import ( - "strconv" - "strings" + "encoding/json" + "os" "sync/atomic" "time" - "github.com/dgraph-io/badger/v2" log "unknwon.dev/clog/v2" ) @@ -49,51 +48,48 @@ func (s *stats) PkgGetIncr(improtPath string, n int64) { atomic.StoreInt64(&s.lastUpdated, time.Now().Unix()) } +// statsData is the structure for JSON serialization +type statsData struct { + TotalView int64 `json:"total_view"` + TotalGet int64 `json:"total_get"` + PkgsView map[string]int64 `json:"pkgs_view"` + PkgsGet map[string]int64 `json:"pkgs_get"` +} + // NOTE: atomic operation is not needed in this method since it is currently only // being called at init time. -func (s *stats) loadFromDB(db *badger.DB) error { - return db.View(func(tx *badger.Txn) error { - iter := tx.NewIterator(badger.DefaultIteratorOptions) - defer iter.Close() - - for iter.Rewind(); iter.Valid(); iter.Next() { - item := iter.Item() - k := item.Key() - err := item.Value(func(v []byte) (err error) { - ks := string(k) - if ks == "view_total" { - s.totalView, _ = strconv.ParseInt(string(v), 10, 64) - return nil - } else if ks == "get_total" { - s.totalGet, _ = strconv.ParseInt(string(v), 10, 64) - return nil - } - - if strings.HasPrefix(ks, "view_") { - importPath := strings.TrimPrefix(ks, "view_") - pkgView, _ := strconv.ParseInt(string(v), 10, 64) - s.pkgsView[importPath] = &pkgView - return nil - } - - if strings.HasPrefix(ks, "get_") { - importPath := strings.TrimPrefix(ks, "get_") - pkgGet, _ := strconv.ParseInt(string(v), 10, 64) - s.pkgsGet[importPath] = &pkgGet - return nil - } - - return nil - }) - if err != nil { - return err - } +func (s *stats) loadFromJSON(path string) error { + data, err := os.ReadFile(path) + if err != nil { + if os.IsNotExist(err) { + // File doesn't exist yet, which is fine + return nil } - return nil - }) + return err + } + + var sd statsData + if err := json.Unmarshal(data, &sd); err != nil { + return err + } + + s.totalView = sd.TotalView + s.totalGet = sd.TotalGet + + for importPath, view := range sd.PkgsView { + v := view + s.pkgsView[importPath] = &v + } + + for importPath, get := range sd.PkgsGet { + g := get + s.pkgsGet[importPath] = &g + } + + return nil } -func (s *stats) start(db *badger.DB, done chan struct{}) { +func (s *stats) start(path string, done chan struct{}) { defer func() { log.Info("Exiting stats syncing goroutine...") done <- struct{}{} @@ -103,48 +99,45 @@ func (s *stats) start(db *badger.DB, done chan struct{}) { for { select { case <-t.C: - s.syncToDB(db) + s.syncToJSON(path) case <-done: - s.syncToDB(db) + s.syncToJSON(path) return } } } -func (s *stats) syncToDB(db *badger.DB) { +func (s *stats) syncToJSON(path string) { lastSynced := atomic.LoadInt64(&s.lastSynced) lastUpdated := atomic.LoadInt64(&s.lastUpdated) if lastSynced == lastUpdated { - log.Trace("stats.syncToDB: nothing changed, DB is up-to-date") + log.Trace("stats.syncToJSON: nothing changed, file is up-to-date") return } - err := db.Update(func(tx *badger.Txn) error { - err := tx.Set([]byte("view_total"), []byte(strconv.FormatInt(s.TotalView(), 10))) - if err != nil { - return err - } + sd := statsData{ + TotalView: s.TotalView(), + TotalGet: s.TotalGet(), + PkgsView: make(map[string]int64), + PkgsGet: make(map[string]int64), + } - err = tx.Set([]byte("get_total"), []byte(strconv.FormatInt(s.TotalGet(), 10))) - if err != nil { - return err - } + for p := range s.pkgsView { + sd.PkgsView[p] = s.PkgView(p) + } - for p := range s.pkgsView { - err = tx.Set([]byte("view_"+p), []byte(strconv.FormatInt(s.PkgView(p), 10))) - if err != nil { - return err - } + for p := range s.pkgsGet { + sd.PkgsGet[p] = s.PkgGet(p) + } - err = tx.Set([]byte("get_"+p), []byte(strconv.FormatInt(s.PkgGet(p), 10))) - if err != nil { - return err - } - } - return nil - }) + data, err := json.MarshalIndent(sd, "", " ") if err != nil { - log.Error("Failed to update DB: %v", err) + log.Error("Failed to marshal stats: %v", err) + return + } + + if err := os.WriteFile(path, data, 0644); err != nil { + log.Error("Failed to write stats file: %v", err) return }