Skip to content

Commit ee74269

Browse files
#45 - Integrate GolangCI-lint (#46)
1 parent 9ac069a commit ee74269

File tree

12 files changed

+301
-73
lines changed

12 files changed

+301
-73
lines changed

.golangci.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
run:
2+
timeout: 5m
3+
modules-download-mode: readonly
4+
5+
linters-settings:
6+
goconst:
7+
min-len: 2
8+
min-occurrences: 2
9+
gofmt:
10+
simplify: true
11+
goimports:
12+
local-prefixes: github.com/mattermost/mattermost-plugin-custom-attributes
13+
golint:
14+
min-confidence: 0
15+
govet:
16+
check-shadowing: true
17+
enable-all: true
18+
misspell:
19+
locale: US
20+
21+
linters:
22+
disable-all: true
23+
enable:
24+
- bodyclose
25+
- deadcode
26+
- errcheck
27+
- goconst
28+
- gocritic
29+
- gofmt
30+
- goimports
31+
- golint
32+
- gosec
33+
- gosimple
34+
- govet
35+
- ineffassign
36+
- interfacer
37+
- misspell
38+
- nakedret
39+
- staticcheck
40+
- structcheck
41+
- stylecheck
42+
- typecheck
43+
- unconvert
44+
- unused
45+
- varcheck
46+
- whitespace
47+
48+
issues:
49+
exclude-rules:
50+
- path: server/manifest.go
51+
linters:
52+
- deadcode
53+
- unused
54+
- varcheck
55+
- path: server/configuration.go
56+
linters:
57+
- unused
58+
- path: _test\.go
59+
linters:
60+
- bodyclose
61+
- goconst
62+
- scopelint # https://github.com/kyoh86/scopelint/issues/4

Makefile

Lines changed: 53 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ GO ?= $(shell command -v go 2> /dev/null)
22
NPM ?= $(shell command -v npm 2> /dev/null)
33
CURL ?= $(shell command -v curl 2> /dev/null)
44
MANIFEST_FILE ?= plugin.json
5+
GOPATH ?= $(shell go env GOPATH)
6+
GO_TEST_FLAGS ?= -race
7+
GO_BUILD_FLAGS ?=
58
MM_UTILITIES_DIR ?= ../mattermost-utilities
69

710
export GO111MODULE=on
@@ -14,6 +17,11 @@ include build/setup.mk
1417

1518
BUNDLE_NAME ?= $(PLUGIN_ID)-$(PLUGIN_VERSION).tar.gz
1619

20+
# Include custom makefile, if present
21+
ifneq ($(wildcard build/custom.mk),)
22+
include build/custom.mk
23+
endif
24+
1725
## Checks the code style, tests, builds and bundles the plugin.
1826
all: check-style test dist
1927

@@ -22,55 +30,34 @@ all: check-style test dist
2230
apply:
2331
./build/bin/manifest apply
2432

25-
## Runs govet and gofmt against all packages.
33+
## Runs golangci-lint and eslint.
2634
.PHONY: check-style
27-
check-style: webapp/.npminstall gofmt govet
35+
check-style: webapp/.npminstall golangci-lint
2836
@echo Checking for style guide compliance
2937

3038
ifneq ($(HAS_WEBAPP),)
3139
cd webapp && npm run lint
3240
endif
3341

34-
## Runs gofmt against all packages.
35-
.PHONY: gofmt
36-
gofmt:
37-
ifneq ($(HAS_SERVER),)
38-
@echo Running gofmt
39-
@for package in $$(go list ./server/...); do \
40-
echo "Checking "$$package; \
41-
files=$$(go list -f '{{range .GoFiles}}{{$$.Dir}}/{{.}} {{end}}' $$package); \
42-
if [ "$$files" ]; then \
43-
gofmt_output=$$(gofmt -d -s $$files 2>&1); \
44-
if [ "$$gofmt_output" ]; then \
45-
echo "$$gofmt_output"; \
46-
echo "Gofmt failure"; \
47-
exit 1; \
48-
fi; \
49-
fi; \
50-
done
51-
@echo Gofmt success
52-
endif
53-
54-
## Runs govet against all packages.
55-
.PHONY: govet
56-
govet:
57-
ifneq ($(HAS_SERVER),)
58-
@echo Running govet
59-
@# Workaroung because you can't install binaries without adding them to go.mod
60-
env GO111MODULE=off $(GO) get golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
61-
$(GO) vet ./server/...
62-
$(GO) vet -vettool=$(GOPATH)/bin/shadow ./server/...
63-
@echo Govet success
64-
endif
42+
## Run golangci-lint on codebase.
43+
.PHONY: golangci-lint
44+
golangci-lint:
45+
@if ! [ -x "$$(command -v golangci-lint)" ]; then \
46+
echo "golangci-lint is not installed. Please see https://github.com/golangci/golangci-lint#install for installation instructions."; \
47+
exit 1; \
48+
fi; \
49+
50+
@echo Running golangci-lint
51+
golangci-lint run ./...
6552

6653
## Builds the server, if it exists, including support for multiple architectures.
6754
.PHONY: server
6855
server:
6956
ifneq ($(HAS_SERVER),)
7057
mkdir -p server/dist;
71-
cd server && env GOOS=linux GOARCH=amd64 $(GO) build -o dist/plugin-linux-amd64;
72-
cd server && env GOOS=darwin GOARCH=amd64 $(GO) build -o dist/plugin-darwin-amd64;
73-
cd server && env GOOS=windows GOARCH=amd64 $(GO) build -o dist/plugin-windows-amd64.exe;
58+
cd server && env GOOS=linux GOARCH=amd64 $(GO) build $(GO_BUILD_FLAGS) -o dist/plugin-linux-amd64;
59+
cd server && env GOOS=darwin GOARCH=amd64 $(GO) build $(GO_BUILD_FLAGS) -o dist/plugin-darwin-amd64;
60+
cd server && env GOOS=windows GOARCH=amd64 $(GO) build $(GO_BUILD_FLAGS) -o dist/plugin-windows-amd64.exe;
7461
endif
7562

7663
## Ensures NPM dependencies are installed without having to run this all the time.
@@ -87,6 +74,14 @@ ifneq ($(HAS_WEBAPP),)
8774
cd webapp && $(NPM) run build;
8875
endif
8976

77+
## Builds the webapp in debug mode, if it exists.
78+
.PHONY: webapp-debug
79+
webapp-debug: webapp/.npminstall
80+
ifneq ($(HAS_WEBAPP),)
81+
cd webapp && \
82+
$(NPM) run debug;
83+
endif
84+
9085
## Generates a tar bundle of the plugin for install.
9186
.PHONY: bundle
9287
bundle:
@@ -116,64 +111,63 @@ endif
116111
dist: apply server webapp bundle
117112

118113
## Installs the plugin to a (development) server.
114+
## It uses the API if appropriate environment variables are defined,
115+
## and otherwise falls back to trying to copy the plugin to a sibling mattermost-server directory.
119116
.PHONY: deploy
120117
deploy: dist
121-
## It uses the API if appropriate environment variables are defined,
122-
## or copying the files directly to a sibling mattermost-server directory.
123-
ifneq ($(and $(MM_SERVICESETTINGS_SITEURL),$(MM_ADMIN_USERNAME),$(MM_ADMIN_PASSWORD),$(CURL)),)
124-
@echo "Installing plugin via API"
125-
$(eval TOKEN := $(shell curl -i -X POST $(MM_SERVICESETTINGS_SITEURL)/api/v4/users/login -d '{"login_id": "$(MM_ADMIN_USERNAME)", "password": "$(MM_ADMIN_PASSWORD)"}' | grep Token | cut -f2 -d' ' 2> /dev/null))
126-
@curl -s -H "Authorization: Bearer $(TOKEN)" -X POST $(MM_SERVICESETTINGS_SITEURL)/api/v4/plugins -F "plugin=@dist/$(BUNDLE_NAME)" -F "force=true" > /dev/null && \
127-
curl -s -H "Authorization: Bearer $(TOKEN)" -X POST $(MM_SERVICESETTINGS_SITEURL)/api/v4/plugins/$(PLUGIN_ID)/enable > /dev/null && \
128-
echo "OK." || echo "Sorry, something went wrong."
129-
else ifneq ($(wildcard ../mattermost-server/.*),)
130-
@echo "Installing plugin via filesystem. Server restart and manual plugin enabling required"
131-
mkdir -p ../mattermost-server/plugins
132-
tar -C ../mattermost-server/plugins -zxvf dist/$(BUNDLE_NAME)
133-
else
134-
@echo "No supported deployment method available. Install plugin manually."
135-
endif
118+
./build/bin/deploy $(PLUGIN_ID) dist/$(BUNDLE_NAME)
119+
120+
.PHONY: debug-deploy
121+
debug-deploy: debug-dist deploy
122+
123+
.PHONY: debug-dist
124+
debug-dist: apply server webapp-debug bundle
136125

137126
## Runs any lints and unit tests defined for the server and webapp, if they exist.
138127
.PHONY: test
139128
test: webapp/.npminstall
140129
ifneq ($(HAS_SERVER),)
141-
$(GO) test -race -v ./server/...
130+
$(GO) test -v $(GO_TEST_FLAGS) ./server/...
142131
endif
143132
ifneq ($(HAS_WEBAPP),)
144-
cd webapp && $(NPM) run fix;
133+
cd webapp && $(NPM) run fix && $(NPM) run test;
145134
endif
146135

147136
## Creates a coverage report for the server code.
148137
.PHONY: coverage
149138
coverage: webapp/.npminstall
150139
ifneq ($(HAS_SERVER),)
151-
$(GO) test -race -coverprofile=server/coverage.txt ./server/...
140+
$(GO) test $(GO_TEST_FLAGS) -coverprofile=server/coverage.txt ./server/...
152141
$(GO) tool cover -html=server/coverage.txt
153142
endif
154143

155144
## Extract strings for translation from the source code.
156145
.PHONY: i18n-extract
157-
i18n-extract:
146+
i18n-extract:
158147
ifneq ($(HAS_WEBAPP),)
159-
@[[ -d $(MM_UTILITIES_DIR) ]] || echo "You must clone github.com/mattermost/mattermost-utilities repo in .. to use this command"
160-
@[[ -d $(MM_UTILITIES_DIR) ]] && cd $(MM_UTILITIES_DIR) && npm install && npm run babel && node mmjstool/build/index.js i18n extract-webapp --webapp-dir ../mattermost-plugin-demo/webapp
148+
ifeq ($(HAS_MM_UTILITIES),)
149+
@echo "You must clone github.com/mattermost/mattermost-utilities repo in .. to use this command"
150+
else
151+
cd $(MM_UTILITIES_DIR) && npm install && npm run babel && node mmjstool/build/index.js i18n extract-webapp --webapp-dir $(PWD)/webapp
152+
endif
161153
endif
162154

163155
## Clean removes all build artifacts.
164156
.PHONY: clean
165157
clean:
166158
rm -fr dist/
167159
ifneq ($(HAS_SERVER),)
160+
rm -fr server/coverage.txt
168161
rm -fr server/dist
169162
endif
170163
ifneq ($(HAS_WEBAPP),)
171164
rm -fr webapp/.npminstall
165+
rm -fr webapp/junit.xml
172166
rm -fr webapp/dist
173167
rm -fr webapp/node_modules
174168
endif
175169
rm -fr build/bin/
176170

177-
# Help documentatin à la https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
171+
# Help documentation à la https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
178172
help:
179-
@cat Makefile | grep -v '\.PHONY' | grep -v '\help:' | grep -B1 -E '^[a-zA-Z0-9_.-]+:.*' | sed -e "s/:.*//" | sed -e "s/^## //" | grep -v '\-\-' | sed '1!G;h;$$!d' | awk 'NR%2{printf "\033[36m%-30s\033[0m",$$0;next;}1' | sort
173+
@cat Makefile | grep -v '\.PHONY' | grep -v '\help:' | grep -B1 -E '^[a-zA-Z0-9_.-]+:.*' | sed -e "s/:.*//" | sed -e "s/^## //" | grep -v '\-\-' | sed '1!G;h;$$!d' | awk 'NR%2{printf "\033[36m%-30s\033[0m",$$0;next;}1' | sort

build/custom.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Include custome targets and environment variables here

build/deploy/main.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// main handles deployment of the plugin to a development server using either the Client4 API
2+
// or by copying the plugin bundle into a sibling mattermost-server/plugin directory.
3+
package main
4+
5+
import (
6+
"fmt"
7+
"log"
8+
"os"
9+
"path/filepath"
10+
11+
"github.com/mattermost/mattermost-server/v5/model"
12+
"github.com/mholt/archiver/v3"
13+
"github.com/pkg/errors"
14+
)
15+
16+
func main() {
17+
err := deploy()
18+
if err != nil {
19+
fmt.Printf("Failed to deploy: %s\n", err.Error())
20+
fmt.Println()
21+
fmt.Println("Usage:")
22+
fmt.Println(" deploy <plugin id> <bundle path>")
23+
os.Exit(1)
24+
}
25+
}
26+
27+
// deploy handles deployment of the plugin to a development server.
28+
func deploy() error {
29+
if len(os.Args) < 3 {
30+
return errors.New("invalid number of arguments")
31+
}
32+
33+
pluginID := os.Args[1]
34+
bundlePath := os.Args[2]
35+
36+
siteURL := os.Getenv("MM_SERVICESETTINGS_SITEURL")
37+
adminToken := os.Getenv("MM_ADMIN_TOKEN")
38+
adminUsername := os.Getenv("MM_ADMIN_USERNAME")
39+
adminPassword := os.Getenv("MM_ADMIN_PASSWORD")
40+
copyTargetDirectory, _ := filepath.Abs("../mattermost-server")
41+
42+
if siteURL != "" {
43+
client := model.NewAPIv4Client(siteURL)
44+
45+
if adminToken != "" {
46+
log.Printf("Authenticating using token against %s.", siteURL)
47+
client.SetToken(adminToken)
48+
49+
return uploadPlugin(client, pluginID, bundlePath)
50+
}
51+
52+
if adminUsername != "" && adminPassword != "" {
53+
client := model.NewAPIv4Client(siteURL)
54+
log.Printf("Authenticating as %s against %s.", adminUsername, siteURL)
55+
_, resp := client.Login(adminUsername, adminPassword)
56+
if resp.Error != nil {
57+
return errors.Wrapf(resp.Error, "failed to login as %s", adminUsername)
58+
}
59+
60+
return uploadPlugin(client, pluginID, bundlePath)
61+
}
62+
}
63+
64+
_, err := os.Stat(copyTargetDirectory)
65+
if os.IsNotExist(err) {
66+
return errors.New("no supported deployment method available, please install plugin manually")
67+
} else if err != nil {
68+
return errors.Wrapf(err, "failed to stat %s", copyTargetDirectory)
69+
}
70+
71+
log.Printf("Installing plugin to mattermost-server found in %s.", copyTargetDirectory)
72+
log.Print("Server restart required to load updated plugin.")
73+
return copyPlugin(pluginID, copyTargetDirectory, bundlePath)
74+
}
75+
76+
// uploadPlugin attempts to upload and enable a plugin via the Client4 API.
77+
// It will fail if plugin uploads are disabled.
78+
func uploadPlugin(client *model.Client4, pluginID, bundlePath string) error {
79+
pluginBundle, err := os.Open(bundlePath)
80+
if err != nil {
81+
return errors.Wrapf(err, "failed to open %s", bundlePath)
82+
}
83+
defer pluginBundle.Close()
84+
85+
log.Print("Uploading plugin via API.")
86+
_, resp := client.UploadPluginForced(pluginBundle)
87+
if resp.Error != nil {
88+
return errors.Wrap(resp.Error, "failed to upload plugin bundle")
89+
}
90+
91+
log.Print("Enabling plugin.")
92+
_, resp = client.EnablePlugin(pluginID)
93+
if resp.Error != nil {
94+
return errors.Wrap(resp.Error, "Failed to enable plugin")
95+
}
96+
97+
return nil
98+
}
99+
100+
// copyPlugin attempts to install a plugin by copying it to a sibling ../mattermost-server/plugin
101+
// directory. A server restart is required before the plugin will start.
102+
func copyPlugin(pluginID, targetPath, bundlePath string) error {
103+
targetPath = filepath.Join(targetPath, "plugins")
104+
105+
err := os.MkdirAll(targetPath, 0777)
106+
if err != nil {
107+
return errors.Wrapf(err, "failed to create %s", targetPath)
108+
}
109+
110+
existingPluginPath := filepath.Join(targetPath, pluginID)
111+
err = os.RemoveAll(existingPluginPath)
112+
if err != nil {
113+
return errors.Wrapf(err, "failed to remove existing existing plugin directory %s", existingPluginPath)
114+
}
115+
116+
err = archiver.Unarchive(bundlePath, targetPath)
117+
if err != nil {
118+
return errors.Wrapf(err, "failed to unarchive %s into %s", bundlePath, targetPath)
119+
}
120+
121+
return nil
122+
}

0 commit comments

Comments
 (0)