diff --git a/.circleci/config.yml b/.circleci/config.yml index 09d44117..b1e3f0f4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,55 +1,39 @@ -version: 2 +version: 2.1 +references: + base_container: &base_container + docker: + - image: filebrowser/builder:v1.1.0 jobs: lint: - docker: - - image: golangci/golangci-lint:v1.31.0 + <<: *base_container steps: - checkout - - run: golangci-lint run -v - build-node: - docker: - - image: circleci/node - steps: - - checkout - - run: - name: "Build" - command: ./wizard.sh -a - - run: - name: "Cleanup" - command: rm -rf frontend/node_modules - - persist_to_workspace: - root: . - paths: - - '*' + - run: make lint test: - docker: - - image: circleci/golang:1.15.2 + <<: *base_container steps: - checkout - run: name: "Test" - command: go test ./... - build-go: - docker: - - image: circleci/golang:1.15.2 + command: make test + build: + <<: *base_container steps: - - attach_workspace: - at: '~/project' + - checkout - run: - name: "Compile" - command: GOOS=linux GOARCH=amd64 ./wizard.sh -c + name: "Build" + command: make build - run: name: "Cleanup" command: | - rm -rf frontend/build - git checkout -- go.sum # TODO: why is it being changed? + rm -rf frontend/node_modules + rm -rf bin/ - persist_to_workspace: root: . paths: - '*' release: - docker: - - image: circleci/golang:1.15.2 + <<: *base_container steps: - attach_workspace: at: '~/project' @@ -69,22 +53,16 @@ workflows: filters: tags: only: /.*/ - - build-node: + - build: filters: tags: only: /.*/ - - build-go: - filters: - tags: - only: /.*/ - requires: - - build-node - - lint - - test - release: context: deploy requires: - - build-go + - build + - test + - lint filters: tags: only: /^v.*/ diff --git a/.gitignore b/.gitignore index 773afff7..d3aecd8d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ yarn-error.log* *.njsproj *.sln *.sw* +bin/ +build/ diff --git a/.golangci.yml b/.golangci.yml index d94a3448..8b71cfed 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -63,7 +63,6 @@ linters: - goconst - gocritic - gocyclo - - gofmt - goimports - golint - gomnd diff --git a/.versionrc b/.versionrc new file mode 100644 index 00000000..ecf59363 --- /dev/null +++ b/.versionrc @@ -0,0 +1,14 @@ +{ + "types": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance improvements" }, + { "type": "revert", "section": "Reverts" }, + { "type": "refactor", "section": "Refactorings" }, + { "type": "build", "section": "Build" }, + { "type": "ci", "hidden": true }, + { "type": "test", "hidden": true }, + { "type": "chore", "hidden": true }, + { "type": "docs", "hidden": true } + ] +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..dea0a2cd --- /dev/null +++ b/Makefile @@ -0,0 +1,98 @@ +SHELL := /bin/bash +BASE_PATH := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +VERSION ?= $(shell git describe --tags --always --match=v* 2> /dev/null || \ + cat $(CURDIR)/.version 2> /dev/null || echo v0) +VERSION_HASH = $(shell git rev-parse HEAD) + +BIN = $(BASE_PATH)/bin +PATH := $(BIN):$(PATH) +export PATH + +# printing +V = 0 +Q = $(if $(filter 1,$V),,@) +M = $(shell printf "\033[34;1m▶\033[0m") + +GO = GOGC=off go +# go module +MODULE = $(shell env GO111MODULE=on $(GO) list -m) + +DATE ?= $(shell date +%FT%T%z) +VERSION ?= $(shell git describe --tags --always --match=v* 2> /dev/null || \ + cat $(CURDIR)/.version 2> /dev/null || echo v0) +VERSION_HASH = $(shell git rev-parse HEAD) +BRANCH = $(shell git rev-parse --abbrev-ref HEAD) + +LDFLAGS += -X "$(MODULE)/varsion.Version=$(VERSION)" -X "$(MODULE)/varsion.CommitSHA=$(VERSION_HASH)" + +# tools +$(BIN): + @mkdir -p $@ +$(BIN)/%: | $(BIN) ; $(info $(M) installing $(PACKAGE)…) + $Q env GOBIN=$(BIN) $(GO) install $(PACKAGE) + +GOLANGCI_LINT = $(BIN)/golangci-lint +$(BIN)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint@v1.37.1 + +GOIMPORTS = $(BIN)/goimports +$(BIN)/goimports: PACKAGE=golang.org/x/tools/cmd/goimports@v0.1.0 + +RICE = $(BIN)/rice +$(BIN)/rice: PACKAGE=github.com/GeertJohan/go.rice/rice@v1.0.2 + +## build: Build +.PHONY: build +build: | build-frontend build-backend ; $(info $(M) building…) + +## build-frontend: Build frontend +.PHONY: build-frontend +build-frontend: | ; $(info $(M) building frontend…) + $Q cd frontend && npm ci && npm run build + +## build-backend: Build backend +.PHONY: build-backend +build-backend: | $(RICE) ; $(info $(M) building backend…) + $Q cd ./http && rm -rf rice-box.go && $(RICE) embed-go + $Q $(GO) build -ldflags '$(LDFLAGS)' -o filebrowser + +## test: Run all tests +.PHONY: test +test: | test-frontend test-backend ; $(info $(M) running tests…) + +## test-frontend: Run frontend tests +.PHONY: test-frontend +test-frontend: | ; $(info $(M) running frontend tests…) + +## test-backend: Run backend tests +.PHONY: test-backend +test-backend: | $(RICE) ; $(info $(M) running backend tests…) + $Q $(GO) test -v ./... + +## lint: Lint +.PHONY: lint +lint: lint-frontend lint-backend lint-commits | ; $(info $(M) running all linters…) + +## lint-frontend: Lint frontend +.PHONY: lint-frontend +lint-frontend: | ; $(info $(M) running frontend linters…) + $Q cd frontend && npm ci && npm run lint + +## lint-backend: Lint backend +.PHONY: lint-backend +lint-backend: | $(GOLANGCI_LINT) ; $(info $(M) running backend linters…) + $Q $(GOLANGCI_LINT) run + +## lint-commits: Lint commits +.PHONY: lint-commits +lint-commits: | ; $(info $(M) running commitlint…) + $Q ./scripts/commitlint.sh + +## bump-version: Bump app version +.PHONY: bump-version +bump-version: | ; $(info $(M) creating a new release…) + $Q ./scripts/bump_version.sh + +## help: Show this help +.PHONY: help +help: + @sed -n 's/^## //p' $(MAKEFILE_LIST) | column -t -s ':' | sed -e 's/^/ /' | sort \ No newline at end of file diff --git a/auth/json.go b/auth/json.go index 925ea4e0..81edfb41 100644 --- a/auth/json.go +++ b/auth/json.go @@ -40,7 +40,7 @@ func (a JSONAuth) Auth(r *http.Request, sto users.Store, root string) (*users.Us // If ReCaptcha is enabled, check the code. if a.ReCaptcha != nil && len(a.ReCaptcha.Secret) > 0 { - ok, err := a.ReCaptcha.Ok(cred.ReCaptcha) //nolint:shadow + ok, err := a.ReCaptcha.Ok(cred.ReCaptcha) //nolint:govet if err != nil { return nil, err diff --git a/cmd/cmds_add.go b/cmd/cmds_add.go index 5706817b..fd2c0dc5 100644 --- a/cmd/cmds_add.go +++ b/cmd/cmds_add.go @@ -14,7 +14,7 @@ var cmdsAddCmd = &cobra.Command{ Use: "add ", Short: "Add a command to run on a specific event", Long: `Add a command to run on a specific event.`, - Args: cobra.MinimumNArgs(2), //nolint:mnd + Args: cobra.MinimumNArgs(2), //nolint:gomnd Run: python(func(cmd *cobra.Command, args []string, d pythonData) { s, err := d.store.Settings.Get() checkErr(err) diff --git a/cmd/cmds_rm.go b/cmd/cmds_rm.go index dc0c06f5..e1f78863 100644 --- a/cmd/cmds_rm.go +++ b/cmd/cmds_rm.go @@ -23,7 +23,7 @@ You can also specify an optional parameter (index_end) so you can remove all commands from 'index' to 'index_end', including 'index_end'.`, Args: func(cmd *cobra.Command, args []string) error { - if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil { //nolint:mnd + if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil { //nolint:gomnd return err } @@ -43,7 +43,7 @@ including 'index_end'.`, i, err := strconv.Atoi(args[1]) checkErr(err) f := i - if len(args) == 3 { //nolint:mnd + if len(args) == 3 { //nolint:gomnd f, err = strconv.Atoi(args[2]) checkErr(err) } diff --git a/cmd/root.go b/cmd/root.go index 75506f78..6108ec96 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -152,12 +152,15 @@ user created with the credentials from options "username" and "password".`, err = os.Chmod(server.Socket, os.FileMode(socketPerm)) checkErr(err) case server.TLSKey != "" && server.TLSCert != "": - cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey) //nolint:shadow + cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey) //nolint:govet checkErr(err) - listener, err = tls.Listen("tcp", adr, &tls.Config{Certificates: []tls.Certificate{cer}}) //nolint:shadow + listener, err = tls.Listen("tcp", adr, &tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{cer}}, + ) checkErr(err) default: - listener, err = net.Listen("tcp", adr) //nolint:shadow + listener, err = net.Listen("tcp", adr) checkErr(err) } diff --git a/cmd/rule_rm.go b/cmd/rule_rm.go index 60b98c93..4bdc87ed 100644 --- a/cmd/rule_rm.go +++ b/cmd/rule_rm.go @@ -44,7 +44,7 @@ including 'index_end'.`, i, err := strconv.Atoi(args[0]) checkErr(err) f := i - if len(args) == 2 { //nolint:mnd + if len(args) == 2 { //nolint:gomnd f, err = strconv.Atoi(args[1]) checkErr(err) } diff --git a/cmd/users_add.go b/cmd/users_add.go index d3b8f825..a51725da 100644 --- a/cmd/users_add.go +++ b/cmd/users_add.go @@ -15,7 +15,7 @@ var usersAddCmd = &cobra.Command{ Use: "add ", Short: "Create a new user", Long: `Create a new user and add it to the database.`, - Args: cobra.ExactArgs(2), //nolint:mnd + Args: cobra.ExactArgs(2), //nolint:gomnd Run: python(func(cmd *cobra.Command, args []string, d pythonData) { s, err := d.store.Settings.Get() checkErr(err) diff --git a/cmd/users_import.go b/cmd/users_import.go index 236e9cea..f0e1a513 100644 --- a/cmd/users_import.go +++ b/cmd/users_import.go @@ -67,7 +67,7 @@ list or set it to 0.`, // with the new username. If there is, print an error and cancel the // operation if user.Username != onDB.Username { - if conflictuous, err := d.store.Users.Get("", user.Username); err == nil { //nolint:shadow + if conflictuous, err := d.store.Users.Get("", user.Username); err == nil { //nolint:govet checkErr(usernameConflictError(user.Username, conflictuous.ID, user.ID)) } } diff --git a/cmd/utils.go b/cmd/utils.go index f0ff8a26..ad27cc03 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -72,7 +72,7 @@ func dbExists(path string) (bool, error) { d := filepath.Dir(path) _, err = os.Stat(d) if os.IsNotExist(err) { - if err := os.MkdirAll(d, 0700); err != nil { //nolint:shadow + if err := os.MkdirAll(d, 0700); err != nil { //nolint:govet return false, err } return false, nil diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 00000000..23d00367 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,34 @@ +module.exports = { + rules: { + 'body-leading-blank': [1, 'always'], + 'body-max-line-length': [2, 'always', 100], + 'footer-leading-blank': [1, 'always'], + 'footer-max-line-length': [2, 'always', 100], + 'header-max-length': [2, 'always', 100], + 'scope-case': [2, 'always', 'lower-case'], + 'subject-case': [ + 2, + 'never', + ['sentence-case', 'start-case', 'pascal-case', 'upper-case'], + ], + 'subject-full-stop': [2, 'never', '.'], + 'type-case': [2, 'always', 'lower-case'], + 'type-empty': [2, 'never'], + 'type-enum': [ + 2, + 'always', + [ + 'feat', + 'fix', + 'perf', + 'revert', + 'refactor', + 'build', + 'ci', + 'test', + 'chore', + 'docs', + ], + ], + }, +}; diff --git a/files/file.go b/files/file.go index fed35dce..9e1eb19d 100644 --- a/files/file.go +++ b/files/file.go @@ -79,7 +79,7 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) { if opts.Expand { if file.IsDir { - if err := file.readListing(opts.Checker, opts.ReadHeader); err != nil { //nolint:shadow + if err := file.readListing(opts.Checker, opts.ReadHeader); err != nil { //nolint:govet return nil, err } return file, nil diff --git a/http/commands.go b/http/commands.go index 6c37adc2..173e57a9 100644 --- a/http/commands.go +++ b/http/commands.go @@ -32,7 +32,7 @@ func wsErr(ws *websocket.Conn, r *http.Request, status int, err error) { //nolin if err != nil || status >= 400 { log.Printf("%s: %v %s %v", r.URL.Path, status, r.RemoteAddr, err) } - if err := ws.WriteControl(websocket.CloseInternalServerErr, []byte(txt), time.Now().Add(WSWriteDeadline)); err != nil { //nolint:shadow + if err := ws.WriteControl(websocket.CloseInternalServerErr, []byte(txt), time.Now().Add(WSWriteDeadline)); err != nil { log.Print(err) } } @@ -47,7 +47,7 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d var raw string for { - _, msg, err := conn.ReadMessage() //nolint:shadow + _, msg, err := conn.ReadMessage() //nolint:govet if err != nil { wsErr(conn, r, http.StatusInternalServerError, err) return 0, nil @@ -60,7 +60,7 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d } if !d.server.EnableExec || !d.user.CanExecute(strings.Split(raw, " ")[0]) { - if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:shadow + if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:govet wsErr(conn, r, http.StatusInternalServerError, err) } @@ -69,7 +69,7 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d command, err := runner.ParseCommand(d.settings, raw) if err != nil { - if err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())); err != nil { //nolint:shadow + if err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())); err != nil { //nolint:govet wsErr(conn, r, http.StatusInternalServerError, err) } return 0, nil diff --git a/http/public_test.go b/http/public_test.go index 19772cfb..34963b5b 100644 --- a/http/public_test.go +++ b/http/public_test.go @@ -70,7 +70,7 @@ func TestPublicShareHandlerAuthentication(t *testing.T) { } t.Cleanup(func() { - if err := db.Close(); err != nil { //nolint:shadow + if err := db.Close(); err != nil { //nolint:govet t.Errorf("failed to close db: %v", err) } }) diff --git a/http/raw.go b/http/raw.go index c65e1d61..10f07bcd 100644 --- a/http/raw.go +++ b/http/raw.go @@ -33,7 +33,7 @@ func parseQueryFiles(r *http.Request, f *files.FileInfo, _ *users.User) ([]strin fileSlice = append(fileSlice, f.Path) } else { for _, name := range names { - name, err := url.QueryUnescape(strings.Replace(name, "+", "%2B", -1)) //nolint:shadow + name, err := url.QueryUnescape(strings.Replace(name, "+", "%2B", -1)) //nolint:govet if err != nil { return nil, err } diff --git a/http/static.go b/http/static.go index d34302cf..d4b18e4f 100644 --- a/http/static.go +++ b/http/static.go @@ -46,7 +46,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, box * if d.settings.Branding.Files != "" { fPath := filepath.Join(d.settings.Branding.Files, "custom.css") - _, err := os.Stat(fPath) //nolint:shadow + _, err := os.Stat(fPath) //nolint:govet if err != nil && !os.IsNotExist(err) { log.Printf("couldn't load custom styles: %v", err) @@ -58,7 +58,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, box * } if d.settings.AuthMethod == auth.MethodJSONAuth { - raw, err := d.store.Auth.Get(d.settings.AuthMethod) //nolint:shadow + raw, err := d.store.Auth.Get(d.settings.AuthMethod) //nolint:govet if err != nil { return http.StatusInternalServerError, err } diff --git a/scripts/bump_version.sh b/scripts/bump_version.sh new file mode 100755 index 00000000..43df6276 --- /dev/null +++ b/scripts/bump_version.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e + +if ! [ -x "$(command -v standard-version)" ]; then + echo "standard-version is not installed. please run 'npm i -g standard-version'" + exit 1 +fi + +standard-version --dry-run --skip +read -p "Continue (y/n)? " -n 1 -r +echo ; +if [[ $REPLY =~ ^[Yy]$ ]]; then + standard-version -s ; +fi \ No newline at end of file diff --git a/scripts/commitlint.sh b/scripts/commitlint.sh new file mode 100755 index 00000000..d5895ce2 --- /dev/null +++ b/scripts/commitlint.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -e + +if ! [ -x "$(command -v commitlint)" ]; then + echo "commitlint is not installed. please run 'npm i -g commitlint'" + exit 1 +fi + +for commit_hash in $(git log --pretty=format:%H origin/master..HEAD); do + commitlint -f ${commit_hash}~1 -t ${commit_hash} +done diff --git a/users/storage.go b/users/storage.go index d18c191e..163082fb 100644 --- a/users/storage.go +++ b/users/storage.go @@ -63,7 +63,7 @@ func (s *Storage) Gets(baseScope string) ([]*User, error) { } for _, user := range users { - if err := user.Clean(baseScope); err != nil { //nolint:shadow + if err := user.Clean(baseScope); err != nil { //nolint:govet return nil, err } } diff --git a/wizard.sh b/wizard.sh deleted file mode 100755 index 2f1d87b8..00000000 --- a/wizard.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env sh - -set -e - -untracked="(untracked)" -REPO=$(cd $(dirname $0); pwd) -COMMIT_SHA=$(git rev-parse --short HEAD) -ASSETS="false" -BINARY="false" -RELEASE="" - -debugInfo () { - echo "Repo: $REPO" - echo "Build assets: $ASSETS" - echo "Build binary: $BINARY" - echo "Release: $RELEASE" -} - -buildAssets () { - cd $REPO - rm -rf frontend/dist - rm -f http/rice-box.go - - cd $REPO/frontend - - if [ "$CI" = "true" ]; then - npm ci - else - npm install - fi - - npm run lint - npm run build -} - -buildBinary () { - if ! [ -x "$(command -v rice)" ]; then - go install github.com/GeertJohan/go.rice/rice - fi - - cd $REPO/http - rm -rf rice-box.go - rice embed-go - - cd $REPO - go build -a -o filebrowser -ldflags "-s -w -X github.com/filebrowser/filebrowser/v2/version.CommitSHA=$COMMIT_SHA" -} - -release () { - cd $REPO - - echo "👀 Checking semver format" - - if [ $# -ne 1 ]; then - echo "❌ This release script requires a single argument corresponding to the semver to be released. See semver.org" - exit 1 - fi - - GREP="grep" - if [ -x "$(command -v ggrep)" ]; then - GREP="ggrep" - fi - - semver=$(echo "$1" | $GREP -P '^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)') - - if [ $? -ne 0 ]; then - echo "❌ Not valid semver format. See semver.org" - exit 1 - fi - - echo "🧼 Tidying up go modules" - go mod tidy - - echo "🐑 Creating a new commit for the new release" - git commit --allow-empty -am "chore: version $semver" - git tag "$1" - git push - git push --tags origin - - echo "📦 Done! $semver released." -} - -usage() { - echo "Usage: $0 [-a] [-c] [-b] [-r ]" 1>&2; - exit 1; -} - -DEBUG="false" - -while getopts "bacr:d" o; do - case "${o}" in - b) - ASSETS="true" - BINARY="true" - ;; - a) - ASSETS="true" - ;; - c) - BINARY="true" - ;; - r) - RELEASE=${OPTARG} - ;; - d) - DEBUG="true" - ;; - *) - usage - ;; - esac -done -shift $((OPTIND-1)) - -if [ "$DEBUG" = "true" ]; then - debugInfo -fi - -if [ "$ASSETS" = "true" ]; then - buildAssets -fi - -if [ "$BINARY" = "true" ]; then - buildBinary -fi - -if [ "$RELEASE" != "" ]; then - release $RELEASE -fi