diff --git a/Makefile b/Makefile index e13d7a8..fc2c58c 100644 --- a/Makefile +++ b/Makefile @@ -195,9 +195,6 @@ endif ifneq ($(HAS_WEBAPP),) cd webapp && $(NPM) run test; endif -ifneq ($(wildcard ./build/sync/plan/.),) - cd ./build/sync && $(GO) test -v $(GO_TEST_FLAGS) ./... -endif ## Creates a coverage report for the server code. .PHONY: coverage @@ -258,15 +255,6 @@ ifneq ($(HAS_WEBAPP),) endif rm -fr build/bin/ -## Sync directory with a starter template -sync: -ifndef STARTERTEMPLATE_PATH - @echo STARTERTEMPLATE_PATH is not set. - @echo Set STARTERTEMPLATE_PATH to a local clone of https://github.com/mattermost/mattermost-plugin-starter-template and retry. - @exit 1 -endif - cd ${STARTERTEMPLATE_PATH} && go run ./build/sync/main.go ./build/sync/plan.yml $(PWD) - # Help documentation à la https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html help: @cat Makefile build/*.mk | 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 diff --git a/build/go.mod b/build/go.mod deleted file mode 100644 index 435e18b..0000000 --- a/build/go.mod +++ /dev/null @@ -1,58 +0,0 @@ -module github.com/mattermost/mattermost-plugin-starter-template/build - -go 1.21 - -require ( - github.com/go-git/go-git/v5 v5.10.1 - github.com/mattermost/mattermost/server/public v0.0.11 - github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.8.4 - sigs.k8s.io/yaml v1.4.0 -) - -require ( - dario.cat/mergo v1.0.0 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect - github.com/blang/semver/v4 v4.0.0 // indirect - github.com/cloudflare/circl v1.3.3 // indirect - github.com/cyphar/filepath-securejoin v0.2.4 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a // indirect - github.com/emirpasic/gods v1.18.1 // indirect - github.com/francoispqt/gojay v1.2.13 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect - github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.5.0 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect - github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 // indirect - github.com/mattermost/logr/v2 v2.0.21 // indirect - github.com/pborman/uuid v1.2.1 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/philhofer/fwd v1.1.2 // indirect - github.com/pjbgf/sha1cd v0.3.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/sergi/go-diff v1.1.0 // indirect - github.com/skeema/knownhosts v1.2.1 // indirect - github.com/tinylib/msgp v1.1.8 // indirect - github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect - github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/wiggin77/merror v1.0.5 // indirect - github.com/wiggin77/srslog v1.0.1 // indirect - github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.15.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.13.0 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/build/go.sum b/build/go.sum deleted file mode 100644 index 726aaec..0000000 --- a/build/go.sum +++ /dev/null @@ -1,347 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a h1:etIrTD8BQqzColk9nKRusM9um5+1q0iOEJLqfBMIK64= -github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a/go.mod h1:emQhSYTXqB0xxjLITTw4EaWZ+8IIQYw+kx9GqNUKdLg= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= -github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= -github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= -github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= -github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= -github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= -github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= -github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.10.1 h1:tu8/D8i+TWxgKpzQ3Vc43e+kkhXqtsZCKI/egajKnxk= -github.com/go-git/go-git/v5 v5.10.1/go.mod h1:uEuHjxkHap8kAl//V5F/nNWwqIYtP/402ddd05mp0wg= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= -github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 h1:Khvh6waxG1cHc4Cz5ef9n3XVCxRWpAKUtqg9PJl5+y8= -github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404/go.mod h1:RyS7FDNQlzF1PsjbJWHRI35exqaKGSO9qD4iv8QjE34= -github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 h1:Y1Tu/swM31pVwwb2BTCsOdamENjjWCI6qmfHLbk6OZI= -github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956/go.mod h1:SRl30Lb7/QoYyohYeVBuqYvvmXSZJxZgiV3Zf6VbxjI= -github.com/mattermost/logr/v2 v2.0.21 h1:CMHsP+nrbRlEC4g7BwOk1GAnMtHkniFhlSQPXy52be4= -github.com/mattermost/logr/v2 v2.0.21/go.mod h1:kZkB/zqKL9e+RY5gB3vGpsyenC+TpuiOenjMkvJJbzc= -github.com/mattermost/mattermost/server/public v0.0.11 h1:Ts4xjan4h9zdgDhGx1uGXutH8IGynl/BCx4zIXOygLo= -github.com/mattermost/mattermost/server/public v0.0.11/go.mod h1:KOO05EB1MwahJTJmPV+cmInnmD4m4BoJQc2a9fmsgmc= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= -github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= -github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= -github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= -github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= -github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= -github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= -github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/wiggin77/merror v1.0.5 h1:P+lzicsn4vPMycAf2mFf7Zk6G9eco5N+jB1qJ2XW3ME= -github.com/wiggin77/merror v1.0.5/go.mod h1:H2ETSu7/bPE0Ymf4bEwdUoo73OOEkdClnoRisfw0Nm0= -github.com/wiggin77/srslog v1.0.1 h1:gA2XjSMy3DrRdX9UqLuDtuVAAshb8bE1NhX1YK0Qe+8= -github.com/wiggin77/srslog v1.0.1/go.mod h1:fehkyYDq1QfuYn60TDPu9YdY2bB85VUW2mvN1WynEls= -github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= -github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/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-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= -gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -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.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/build/pluginctl/main.go b/build/pluginctl/main.go index 1defc78..54572fc 100644 --- a/build/pluginctl/main.go +++ b/build/pluginctl/main.go @@ -8,10 +8,13 @@ import ( "log" "net" "os" + "time" "github.com/mattermost/mattermost/server/public/model" ) +const commandTimeout = 120 * time.Second + const helpText = ` Usage: pluginctl deploy @@ -34,7 +37,10 @@ func pluginctl() error { return errors.New("invalid number of arguments") } - client, err := getClient() + ctx, cancel := context.WithTimeout(context.Background(), commandTimeout) + defer cancel() + + client, err := getClient(ctx) if err != nil { return err } @@ -44,19 +50,19 @@ func pluginctl() error { if len(os.Args) < 4 { return errors.New("invalid number of arguments") } - return deploy(client, os.Args[2], os.Args[3]) + return deploy(ctx, client, os.Args[2], os.Args[3]) case "disable": - return disablePlugin(client, os.Args[2]) + return disablePlugin(ctx, client, os.Args[2]) case "enable": - return enablePlugin(client, os.Args[2]) + return enablePlugin(ctx, client, os.Args[2]) case "reset": - return resetPlugin(client, os.Args[2]) + return resetPlugin(ctx, client, os.Args[2]) default: return errors.New("invalid second argument") } } -func getClient() (*model.Client4, error) { +func getClient(ctx context.Context) (*model.Client4, error) { socketPath := os.Getenv("MM_LOCALSOCKETPATH") if socketPath == "" { socketPath = model.LocalModeSocketPath @@ -92,7 +98,7 @@ func getClient() (*model.Client4, error) { if adminUsername != "" && adminPassword != "" { client := model.NewAPIv4Client(siteURL) log.Printf("Authenticating as %s against %s.", adminUsername, siteURL) - _, _, err := client.Login(context.TODO(), adminUsername, adminPassword) + _, _, err := client.Login(ctx, adminUsername, adminPassword) if err != nil { return nil, fmt.Errorf("failed to login as %s: %w", adminUsername, err) } @@ -114,7 +120,7 @@ func getUnixClient(socketPath string) (*model.Client4, bool) { // deploy attempts to upload and enable a plugin via the Client4 API. // It will fail if plugin uploads are disabled. -func deploy(client *model.Client4, pluginID, bundlePath string) error { +func deploy(ctx context.Context, client *model.Client4, pluginID, bundlePath string) error { pluginBundle, err := os.Open(bundlePath) if err != nil { return fmt.Errorf("failed to open %s: %w", bundlePath, err) @@ -122,13 +128,13 @@ func deploy(client *model.Client4, pluginID, bundlePath string) error { defer pluginBundle.Close() log.Print("Uploading plugin via API.") - _, _, err = client.UploadPluginForced(context.TODO(), pluginBundle) + _, _, err = client.UploadPluginForced(ctx, pluginBundle) if err != nil { return fmt.Errorf("failed to upload plugin bundle: %s", err.Error()) } log.Print("Enabling plugin.") - _, err = client.EnablePlugin(context.TODO(), pluginID) + _, err = client.EnablePlugin(ctx, pluginID) if err != nil { return fmt.Errorf("failed to enable plugin: %s", err.Error()) } @@ -137,9 +143,9 @@ func deploy(client *model.Client4, pluginID, bundlePath string) error { } // disablePlugin attempts to disable the plugin via the Client4 API. -func disablePlugin(client *model.Client4, pluginID string) error { +func disablePlugin(ctx context.Context, client *model.Client4, pluginID string) error { log.Print("Disabling plugin.") - _, err := client.DisablePlugin(context.TODO(), pluginID) + _, err := client.DisablePlugin(ctx, pluginID) if err != nil { return fmt.Errorf("failed to disable plugin: %w", err) } @@ -148,9 +154,9 @@ func disablePlugin(client *model.Client4, pluginID string) error { } // enablePlugin attempts to enable the plugin via the Client4 API. -func enablePlugin(client *model.Client4, pluginID string) error { +func enablePlugin(ctx context.Context, client *model.Client4, pluginID string) error { log.Print("Enabling plugin.") - _, err := client.EnablePlugin(context.TODO(), pluginID) + _, err := client.EnablePlugin(ctx, pluginID) if err != nil { return fmt.Errorf("failed to enable plugin: %w", err) } @@ -159,13 +165,13 @@ func enablePlugin(client *model.Client4, pluginID string) error { } // resetPlugin attempts to reset the plugin via the Client4 API. -func resetPlugin(client *model.Client4, pluginID string) error { - err := disablePlugin(client, pluginID) +func resetPlugin(ctx context.Context, client *model.Client4, pluginID string) error { + err := disablePlugin(ctx, client, pluginID) if err != nil { return err } - err = enablePlugin(client, pluginID) + err = enablePlugin(ctx, client, pluginID) if err != nil { return err } diff --git a/build/sync/README.md b/build/sync/README.md deleted file mode 100644 index 34c834f..0000000 --- a/build/sync/README.md +++ /dev/null @@ -1,113 +0,0 @@ -sync -==== - -The sync tool is a proof-of-concept implementation of a tool for synchronizing mattermost plugin -repositories with the mattermost-plugin-starter-template repo. - -Overview --------- - -At its core the tool is just a collection of checks and actions that are executed according to a -synchronization plan (see [./build/sync/plan.yml](https://github.com/mattermost/mattermost-plugin-starter-template/blob/sync/build/sync/plan.yml) -for an example). The plan defines a set of files -and/or directories that need to be kept in sync between the plugin repository and the template (this -repo). - -For each set of paths, a set of actions to be performed is outlined. No more than one action of that set -will be executed - the first one whose checks pass. Other actions are meant to act as fallbacks. -The idea is to be able to e.g. overwrite a file if it has no local changes or apply a format-specific -merge algorithm otherwise. - -Before running each action, the tool will check if any checks are defined for that action. If there -are any, they will be executed and their results examined. If all checks pass, the action will be executed. -If there is a check failure, the tool will locate the next applicable action according to the plan and -start over with it. - -The synchronization plan can also run checks before running any actions, e.g. to check if the source and -target worktrees are clean. - -Running -------- - -The tool can be executed from the root of this repository with a command: -``` -$ go run ./build/sync/main.go ./build/sync/plan.yml ../mattermost-plugin-github -``` - -(assuming `mattermost-plugin-github` is the target repository we want to synchronize with the source). - -plan.yml ---------- - -The `plan.yml` file (located in `build/sync/plan.yml`) consists of two parts: - - checks - - actions - -The `checks` section defines tests to run before executing the plan itself. Currently the only available such check is `repo_is_clean` defined as: -``` -type: repo_is_clean -params: - repo: source -``` -The `repo` parameter takes one of two values: -- `source` - the `mattermost-plugin-starter-template` repository -- `target` - the repository of the plugin being updated. - -The `actions` section defines actions to be run as part of the synchronization. -Each entry in this section has the form: -``` -paths: - - path1 - - path2 -actions: - - type: action_type - params: - action_parameter: value - conditions: - - type: check_type - params: - check_parameter: value -``` - -`paths` is a list of file or directory paths (relative to the root of the repository) -synchronization should be performed on. - -Each action in the `actions` section is defined by its type. Currently supported action types are: - - `overwrite_file` - overwrite the specified file in the `target` repository with the file in the `source` repository. - - `overwrite_directory` - overwrite a directory. - -Both actions accept a parameter called `create` which determines if the file or directory should be created if it does not exist in the target repository. - -The `conditions` part of an action definition defines tests that need to pass for the -action to be run. Available checks are: - - `exists` - - `file_unaltered` - -The `exists` check takes a single parameter - `repo` (referencing either the source or target repository) and it passes only if the file or directory the action is about to be run on exists. If the repo parameter is not specified, it will default to `target`. - -The `file_unaltered` check is only applicable to file paths. It passes if the file -has not been altered - i.e. it is identical to some version of that same file in the reference repository (usually `source`). This check takes two parameters: - - `in` - repository to check the file in, default `target` - - `compared-to` - repository to check the file against, default `source`. - -When multiple actions are specified for a set of paths, the `sync` tool will only -execute a single action for each path. The first action in the list, whose conditions -are all satisfied will be executed. - -If an acton fails due to an error, the synchronization run will be aborted. - -Caveat emptor -------------- - -This is a very basic proof-of-concept and there are many things that should be improved/implemented: -(in no specific order) - - 1. Format-specific merge actions for `go.mod`, `go.sum`, `webapp/package.json` and other files should - be implemented. - 2. Better logging should be implemented. - 3. Handling action dependencies should be investigated. - e.g. if the `build` directory is overwritten, that will in some cases mean that the go.mod file also needs - to be updated. - 4. Storing the tree-hash of the template repository that the plugin was synchronized with would allow - improving the performance of the tool by restricting the search space when examining if a file - has been altered in the plugin repository. diff --git a/build/sync/main.go b/build/sync/main.go deleted file mode 100644 index f03f9a8..0000000 --- a/build/sync/main.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "io/ioutil" - "os" - "path/filepath" - - "sigs.k8s.io/yaml" - - "github.com/mattermost/mattermost-plugin-starter-template/build/sync/plan" -) - -func main() { - verbose := flag.Bool("verbose", false, "enable verbose output") - flag.Usage = func() { - fmt.Fprintf(flag.CommandLine.Output(), "Update a pluging directory with /mattermost-plugin-starter-template/.\n") - fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), "%s \n", os.Args[0]) - flag.PrintDefaults() - } - - flag.Parse() - // TODO: implement proper command line parameter parsing. - if len(os.Args) != 3 { - fmt.Fprintf(os.Stderr, "running: \n $ sync [plan.yaml] [plugin path]\n") - os.Exit(1) - } - - syncPlan, err := readPlan(os.Args[1]) - if err != nil { - fmt.Fprintf(os.Stderr, "coud not load plan: %s\n", err) - os.Exit(1) - } - - srcDir, err := os.Getwd() - if err != nil { - fmt.Fprintf(os.Stderr, "failed to get current directory: %s\n", err) - os.Exit(1) - } - - trgDir, err := filepath.Abs(os.Args[2]) - if err != nil { - fmt.Fprintf(os.Stderr, "could not determine target directory: %s\n", err) - os.Exit(1) - } - - srcRepo, err := plan.GetRepoSetup(srcDir) - if err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err) - os.Exit(1) - } - trgRepo, err := plan.GetRepoSetup(trgDir) - if err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err) - os.Exit(1) - } - - planSetup := plan.Setup{ - Source: srcRepo, - Target: trgRepo, - VerboseLogging: *verbose, - } - err = syncPlan.Execute(planSetup) - if err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err) - os.Exit(1) - } -} - -func readPlan(path string) (*plan.Plan, error) { - raw, err := ioutil.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("failed to read plan file %q: %v", path, err) - } - - var p plan.Plan - err = yaml.Unmarshal(raw, &p) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal plan yaml: %v", err) - } - - return &p, err -} diff --git a/build/sync/plan.yml b/build/sync/plan.yml deleted file mode 100644 index c31bfb0..0000000 --- a/build/sync/plan.yml +++ /dev/null @@ -1,44 +0,0 @@ -checks: - - type: repo_is_clean - params: - repo: source - - type: repo_is_clean - params: - repo: target -actions: - - paths: - - build/pluginctl - - build/manifest - actions: - - type: overwrite_directory - params: - create: true - - paths: - - Makefile - actions: - - type: overwrite_file - params: - create: true - - paths: - - .editorconfig - - .gitattributes - - .gitignore - - build/.gitignore - - build/go.mod - - build/go.sum - - build/setup.mk - - server/.gitignore - - webapp/.eslintrc.json - - webapp/.npmrc - - webapp/babel.config.js - - webapp/package.json - - webapp/tsconfig.json - - webapp/webpack.config.js - - webapp/src/manifest.test.tsx - - webapp/tests/setup.tsx - actions: - - type: overwrite_file - params: - create: true - conditions: - - type: file_unaltered diff --git a/build/sync/plan/actions.go b/build/sync/plan/actions.go deleted file mode 100644 index 0f08c73..0000000 --- a/build/sync/plan/actions.go +++ /dev/null @@ -1,214 +0,0 @@ -package plan - -import ( - "fmt" - "io" - "os" - "path/filepath" - "strings" -) - -// ActionConditions adds condition support to actions. -type ActionConditions struct { - // Conditions are checkers run before executing the - // action. If any one fails (returns an error), the action - // itself is not executed. - Conditions []Check -} - -// Check runs the conditions associated with the action and returns -// the first error (if any). -func (c ActionConditions) Check(path string, setup Setup) error { - if len(c.Conditions) > 0 { - setup.Logf("checking action conditions") - } - for _, condition := range c.Conditions { - err := condition.Check(path, setup) - if err != nil { - return err - } - } - return nil -} - -// OverwriteFileAction is used to overwrite a file. -type OverwriteFileAction struct { - ActionConditions - Params struct { - // Create determines whether the target directory - // will be created if it does not exist. - Create bool `json:"create"` - } -} - -// Run implements plan.Action.Run. -func (a OverwriteFileAction) Run(path string, setup Setup) error { - setup.Logf("overwriting file %q", path) - src := setup.PathInRepo(SourceRepo, path) - dst := setup.PathInRepo(TargetRepo, path) - - dstInfo, err := os.Stat(dst) - switch { - case os.IsNotExist(err): - if !a.Params.Create { - return fmt.Errorf("path %q does not exist, not creating", dst) - } - case err != nil: - return fmt.Errorf("failed to check path %q: %v", dst, err) - case dstInfo.IsDir(): - return fmt.Errorf("path %q is a directory", dst) - } - - srcInfo, err := os.Stat(src) - if os.IsNotExist(err) { - return fmt.Errorf("file %q does not exist", src) - } else if err != nil { - return fmt.Errorf("failed to check path %q: %v", src, err) - } - if srcInfo.IsDir() { - return fmt.Errorf("path %q is a directory", src) - } - - srcF, err := os.Open(src) - if err != nil { - return fmt.Errorf("failed to open %q: %v", src, err) - } - defer srcF.Close() - dstF, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, srcInfo.Mode()) - if err != nil { - return fmt.Errorf("failed to open %q: %v", src, err) - } - defer dstF.Close() - _, err = io.Copy(dstF, srcF) - if err != nil { - return fmt.Errorf("failed to copy file %q: %v", path, err) - } - return nil -} - -// OverwriteDirectoryAction is used to completely overwrite directories. -// If the target directory exists, it will be removed first. -type OverwriteDirectoryAction struct { - ActionConditions - Params struct { - // Create determines whether the target directory - // will be created if it does not exist. - Create bool `json:"create"` - } -} - -// Run implements plan.Action.Run. -func (a OverwriteDirectoryAction) Run(path string, setup Setup) error { - setup.Logf("overwriting directory %q", path) - src := setup.PathInRepo(SourceRepo, path) - dst := setup.PathInRepo(TargetRepo, path) - - dstInfo, err := os.Stat(dst) - switch { - case os.IsNotExist(err): - if !a.Params.Create { - return fmt.Errorf("path %q does not exist, not creating", dst) - } - case err != nil: - return fmt.Errorf("failed to check path %q: %v", dst, err) - default: - if !dstInfo.IsDir() { - return fmt.Errorf("path %q is not a directory", dst) - } - err = os.RemoveAll(dst) - if err != nil { - return fmt.Errorf("failed to remove directory %q: %v", dst, err) - } - } - - srcInfo, err := os.Stat(src) - if os.IsNotExist(err) { - return fmt.Errorf("directory %q does not exist", src) - } else if err != nil { - return fmt.Errorf("failed to check path %q: %v", src, err) - } - if !srcInfo.IsDir() { - return fmt.Errorf("path %q is not a directory", src) - } - - err = CopyDirectory(src, dst) - if err != nil { - return fmt.Errorf("failed to copy path %q: %v", path, err) - } - return nil -} - -// CopyDirectory copies the directory src to dst so that after -// a successful operation the contents of src and dst are equal. -func CopyDirectory(src, dst string) error { - copier := dirCopier{dst: dst, src: src} - return filepath.Walk(src, copier.Copy) -} - -type dirCopier struct { - dst string - src string -} - -// Convert a path in the source directory to a path in the destination -// directory. -func (d dirCopier) srcToDst(path string) (string, error) { - suff := strings.TrimPrefix(path, d.src) - if suff == path { - return "", fmt.Errorf("path %q is not in %q", path, d.src) - } - return filepath.Join(d.dst, suff), nil -} - -// Copy is an implementation of filepatch.WalkFunc that copies the -// source directory to target with all subdirectories. -func (d dirCopier) Copy(srcPath string, info os.FileInfo, err error) error { - if err != nil { - return fmt.Errorf("failed to copy directory: %v", err) - } - trgPath, err := d.srcToDst(srcPath) - if err != nil { - return err - } - if info.IsDir() { - err = os.MkdirAll(trgPath, info.Mode()) - if err != nil { - return fmt.Errorf("failed to create directory %q: %v", trgPath, err) - } - err = os.Chtimes(trgPath, info.ModTime(), info.ModTime()) - if err != nil { - return fmt.Errorf("failed to create directory %q: %v", trgPath, err) - } - return nil - } - err = copyFile(srcPath, trgPath, info) - if err != nil { - return fmt.Errorf("failed to copy file %q: %v", srcPath, err) - } - return nil -} - -func copyFile(src, dst string, info os.FileInfo) error { - srcF, err := os.Open(src) - if err != nil { - return fmt.Errorf("failed to open source file %q: %v", src, err) - } - defer srcF.Close() - dstF, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, info.Mode()) - if err != nil { - return fmt.Errorf("failed to open destination file %q: %v", dst, err) - } - _, err = io.Copy(dstF, srcF) - if err != nil { - dstF.Close() - return fmt.Errorf("failed to copy file %q: %v", src, err) - } - if err = dstF.Close(); err != nil { - return fmt.Errorf("failed to close file %q: %v", dst, err) - } - err = os.Chtimes(dst, info.ModTime(), info.ModTime()) - if err != nil { - return fmt.Errorf("failed to adjust file modification time for %q: %v", dst, err) - } - return nil -} diff --git a/build/sync/plan/actions_test.go b/build/sync/plan/actions_test.go deleted file mode 100644 index 9f8152d..0000000 --- a/build/sync/plan/actions_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package plan_test - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/mattermost/mattermost-plugin-starter-template/build/sync/plan" -) - -func TestCopyDirectory(t *testing.T) { - assert := assert.New(t) - - // Create a temporary directory to copy to. - dir, err := ioutil.TempDir("", "test") - assert.Nil(err) - defer os.RemoveAll(dir) - - wd, err := os.Getwd() - assert.Nil(err) - - srcDir := filepath.Join(wd, "testdata") - err = plan.CopyDirectory(srcDir, dir) - assert.Nil(err) - - compareDirectories(t, dir, srcDir) -} - -func TestOverwriteFileAction(t *testing.T) { - assert := assert.New(t) - - // Create a temporary directory to copy to. - dir, err := ioutil.TempDir("", "test") - assert.Nil(err) - defer os.RemoveAll(dir) - - wd, err := os.Getwd() - assert.Nil(err) - - setup := plan.Setup{ - Source: plan.RepoSetup{ - Git: nil, - Path: filepath.Join(wd, "testdata", "b"), - }, - Target: plan.RepoSetup{ - Git: nil, - Path: dir, - }, - } - action := plan.OverwriteFileAction{} - action.Params.Create = true - err = action.Run("c", setup) - assert.Nil(err) - - compareDirectories(t, dir, filepath.Join(wd, "testdata", "b")) -} - -func TestOverwriteDirectoryAction(t *testing.T) { - assert := assert.New(t) - - // Create a temporary directory to copy to. - dir, err := ioutil.TempDir("", "test") - assert.Nil(err) - defer os.RemoveAll(dir) - - wd, err := os.Getwd() - assert.Nil(err) - - setup := plan.Setup{ - Source: plan.RepoSetup{ - Git: nil, - Path: wd, - }, - Target: plan.RepoSetup{ - Git: nil, - Path: dir, - }, - } - action := plan.OverwriteDirectoryAction{} - action.Params.Create = true - err = action.Run("testdata", setup) - assert.Nil(err) - - destDir := filepath.Join(dir, "testdata") - srcDir := filepath.Join(wd, "testdata") - compareDirectories(t, destDir, srcDir) -} - -func compareDirectories(t *testing.T, pathA, pathB string) { - assert := assert.New(t) - t.Helper() - - aContents, err := ioutil.ReadDir(pathA) - assert.Nil(err) - bContents, err := ioutil.ReadDir(pathB) - assert.Nil(err) - assert.Len(aContents, len(bContents)) - - // Check the directory contents are equal. - for i, aFInfo := range aContents { - bFInfo := bContents[i] - assert.Equal(aFInfo.Name(), bFInfo.Name()) - assert.Equal(aFInfo.Mode(), bFInfo.Mode()) - assert.Equal(aFInfo.IsDir(), bFInfo.IsDir()) - if !aFInfo.IsDir() { - assert.Equal(aFInfo.Size(), bFInfo.Size()) - } - } -} diff --git a/build/sync/plan/checks.go b/build/sync/plan/checks.go deleted file mode 100644 index a65a255..0000000 --- a/build/sync/plan/checks.go +++ /dev/null @@ -1,176 +0,0 @@ -package plan - -import ( - "fmt" - "os" - "sort" - - "github.com/pkg/errors" - - "github.com/mattermost/mattermost-plugin-starter-template/build/sync/plan/git" -) - -// CheckFail is a custom error type used to indicate a -// check that did not pass (but did not fail due to external -// causes. -// Use `IsCheckFail` to check if an error is a check failure. -type CheckFail string - -func (e CheckFail) Error() string { - return string(e) -} - -// CheckFailf creates an error with the specified message string. -// The error will pass the IsCheckFail filter. -func CheckFailf(msg string, args ...interface{}) CheckFail { - if len(args) > 0 { - msg = fmt.Sprintf(msg, args...) - } - return CheckFail(msg) -} - -// IsCheckFail determines if an error is a check fail error. -func IsCheckFail(err error) bool { - if err == nil { - return false - } - _, ok := err.(CheckFail) - return ok -} - -// RepoIsCleanChecker checks whether the git repository is clean. -type RepoIsCleanChecker struct { - Params struct { - Repo RepoID - } -} - -// Check implements the Checker interface. -// The path parameter is ignored because this checker checks the state of a repository. -func (r RepoIsCleanChecker) Check(_ string, ctx Setup) error { - ctx.Logf("checking if repository %q is clean", r.Params.Repo) - rc := ctx.GetRepo(r.Params.Repo) - repo := rc.Git - worktree, err := repo.Worktree() - if err != nil { - return fmt.Errorf("failed to get worktree: %v", err) - } - status, err := worktree.Status() - if err != nil { - return fmt.Errorf("failed to get worktree status: %v", err) - } - if !status.IsClean() { - return CheckFailf("%q repository is not clean", r.Params.Repo) - } - return nil -} - -// PathExistsChecker checks whether the fle or directory with the -// path exists. If it does not, an error is returned. -type PathExistsChecker struct { - Params struct { - Repo RepoID - } -} - -// Check implements the Checker interface. -func (r PathExistsChecker) Check(path string, ctx Setup) error { - repo := r.Params.Repo - if repo == "" { - repo = TargetRepo - } - ctx.Logf("checking if path %q exists in repo %q", path, repo) - absPath := ctx.PathInRepo(repo, path) - _, err := os.Stat(absPath) - if os.IsNotExist(err) { - return CheckFailf("path %q does not exist", path) - } else if err != nil { - return fmt.Errorf("failed to stat path %q: %v", absPath, err) - } - return nil -} - -// FileUnalteredChecker checks whether the file in Repo is -// an unaltered version of that same file in ReferenceRepo. -// -// Its purpose is to check that a file has not been changed after forking a repository. -// It could be an old unaltered version, so the git history of the file is traversed -// until a matching version is found. -// -// If the repositories in the parameters are not specified, -// reference will default to the source repository and repo - to the target. -type FileUnalteredChecker struct { - Params struct { - SourceRepo RepoID `json:"compared-to"` - TargetRepo RepoID `json:"in"` - } -} - -// Check implements the Checker interface. -func (f FileUnalteredChecker) Check(path string, setup Setup) error { - setup.Logf("checking if file %q has not been altered", path) - repo := f.Params.TargetRepo - if repo == "" { - repo = TargetRepo - } - source := f.Params.SourceRepo - if source == "" { - source = SourceRepo - } - trgPath := setup.PathInRepo(repo, path) - srcPath := setup.PathInRepo(source, path) - - fileHashes, err := git.FileHistory(path, setup.GetRepo(source).Git) - if err != nil { - return err - } - - var srcDeleted bool - srcInfo, err := os.Stat(srcPath) - if err != nil { - if os.IsNotExist(err) { - srcDeleted = true - } else { - return fmt.Errorf("failed to get stat for %q: %v", trgPath, err) - } - } else if srcInfo.IsDir() { - return fmt.Errorf("%q is a directory in source repository", path) - } - - trgInfo, err := os.Stat(trgPath) - if os.IsNotExist(err) { - if srcDeleted { - // File has been deleted in target and source repositories. - // Consider it unaltered. - return nil - } - // Check if the file was ever in git history. - _, err := git.FileHistory(path, setup.GetRepo(repo).Git) - if errors.Is(err, git.ErrNotFound) { - // This is a new file being introduced to the target repo. - // Consider it unaltered. - return nil - } else if err != nil { - return err - } - return CheckFailf("file %q has been deleted", trgPath) - } - if err != nil { - return fmt.Errorf("failed to get stat for %q: %v", trgPath, err) - } - if trgInfo.IsDir() { - return fmt.Errorf("%q is a directory", trgPath) - } - - currentHash, err := git.GetFileHash(trgPath) - if err != nil { - return err - } - - sort.Strings(fileHashes) - idx := sort.SearchStrings(fileHashes, currentHash) - if idx < len(fileHashes) && fileHashes[idx] == currentHash { - return nil - } - return CheckFailf("file %q has been altered", trgPath) -} diff --git a/build/sync/plan/checks_test.go b/build/sync/plan/checks_test.go deleted file mode 100644 index 3814400..0000000 --- a/build/sync/plan/checks_test.go +++ /dev/null @@ -1,213 +0,0 @@ -package plan_test - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "path/filepath" - "testing" - "time" - - git "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/object" - "github.com/stretchr/testify/assert" - - "github.com/mattermost/mattermost-plugin-starter-template/build/sync/plan" -) - -// Tests for the RepoIsClean checker. -func TestRepoIsCleanChecker(t *testing.T) { - assert := assert.New(t) - - // Create a git repository in a temporary dir. - dir, err := ioutil.TempDir("", "test") - assert.Nil(err) - defer os.RemoveAll(dir) - repo, err := git.PlainInit(dir, false) - assert.Nil(err) - - // Repo should be clean. - checker := plan.RepoIsCleanChecker{} - checker.Params.Repo = plan.TargetRepo - - ctx := plan.Setup{ - Target: plan.RepoSetup{ - Path: dir, - Git: repo, - }, - } - assert.Nil(checker.Check("", ctx)) - - // Create a file in the repository. - err = ioutil.WriteFile(path.Join(dir, "data.txt"), []byte("lorem ipsum"), 0600) - assert.Nil(err) - err = checker.Check("", ctx) - assert.EqualError(err, "\"target\" repository is not clean") - assert.True(plan.IsCheckFail(err)) -} - -func TestPathExistsChecker(t *testing.T) { - assert := assert.New(t) - - // Set up a working directory. - wd, err := ioutil.TempDir("", "repo") - assert.Nil(err) - defer os.RemoveAll(wd) - err = os.Mkdir(filepath.Join(wd, "t"), 0755) - assert.Nil(err) - err = ioutil.WriteFile(filepath.Join(wd, "t", "test"), []byte("lorem ipsum"), 0644) - assert.Nil(err) - - checker := plan.PathExistsChecker{} - checker.Params.Repo = plan.SourceRepo - - ctx := plan.Setup{ - Source: plan.RepoSetup{ - Path: wd, - }, - } - - // Check with existing directory. - assert.Nil(checker.Check("t", ctx)) - - // Check with existing file. - assert.Nil(checker.Check("t/test", ctx)) - - err = checker.Check("nosuchpath", ctx) - assert.NotNil(err) - assert.True(plan.IsCheckFail(err)) -} - -func tempGitRepo(assert *assert.Assertions) (string, *git.Repository, func()) { - // Setup repository. - wd, err := ioutil.TempDir("", "repo") - assert.Nil(err) - - // Initialize a repository. - repo, err := git.PlainInit(wd, false) - assert.Nil(err) - w, err := repo.Worktree() - assert.Nil(err) - // Create repository files. - err = ioutil.WriteFile(filepath.Join(wd, "test"), - []byte("lorem ipsum"), 0644) - assert.Nil(err) - sig := &object.Signature{ - Name: "test", - Email: "test@example.com", - When: time.Now(), - } - _, err = w.Commit("initial commit", &git.CommitOptions{Author: sig}) - assert.Nil(err) - pathA := "a.txt" - err = ioutil.WriteFile(filepath.Join(wd, pathA), - []byte("lorem ipsum"), 0644) - assert.Nil(err) - _, err = w.Add(pathA) - assert.Nil(err) - _, err = w.Commit("add files", &git.CommitOptions{Author: sig}) - assert.Nil(err) - - return wd, repo, func() { os.RemoveAll(wd) } - -} - -func TestUnalteredCheckerSameFile(t *testing.T) { - assert := assert.New(t) - - wd, repo, cleanup := tempGitRepo(assert) - defer cleanup() - - ctx := plan.Setup{ - Source: plan.RepoSetup{ - Path: wd, - Git: repo, - }, - Target: plan.RepoSetup{ - Path: wd, - }, - } - - checker := plan.FileUnalteredChecker{} - checker.Params.SourceRepo = plan.SourceRepo - checker.Params.TargetRepo = plan.TargetRepo - - // Check with the same file - check should succeed - hashPath := "a.txt" - err := checker.Check(hashPath, ctx) - assert.Nil(err) -} - -func TestUnalteredCheckerDifferentContents(t *testing.T) { - assert := assert.New(t) - - wd, repo, cleanup := tempGitRepo(assert) - defer cleanup() - - ctx := plan.Setup{ - Source: plan.RepoSetup{ - Path: wd, - Git: repo, - }, - Target: plan.RepoSetup{ - Path: wd, - }, - } - - checker := plan.FileUnalteredChecker{} - checker.Params.SourceRepo = plan.SourceRepo - checker.Params.TargetRepo = plan.TargetRepo - - // Create a file with the same suffix path, but different contents. - tmpDir, err := ioutil.TempDir("", "test") - assert.Nil(err) - defer os.RemoveAll(tmpDir) - err = ioutil.WriteFile(filepath.Join(tmpDir, "a.txt"), - []byte("not lorem ipsum"), 0644) - assert.Nil(err) - - // Set the plugin path to the temporary directory. - ctx.Target.Path = tmpDir - err = checker.Check("a.txt", ctx) - assert.True(plan.IsCheckFail(err)) - assert.EqualError(err, fmt.Sprintf("file %q has been altered", filepath.Join(tmpDir, "a.txt"))) - -} - -// TestUnalteredCheckerNonExistant tests running the unaltered file checker -// in the case where the target file does not exist. If the files has no history, -// the checker should pass. -func TestUnalteredCheckerNonExistant(t *testing.T) { - assert := assert.New(t) - hashPath := "a.txt" - - wd, repo, cleanup := tempGitRepo(assert) - defer cleanup() - - // Temporary repo. - tmpDir, err := ioutil.TempDir("", "test") - assert.Nil(err) - defer os.RemoveAll(tmpDir) - - trgRepo, err := git.PlainInit(tmpDir, false) - assert.Nil(err) - - ctx := plan.Setup{ - Source: plan.RepoSetup{ - Path: wd, - Git: repo, - }, - Target: plan.RepoSetup{ - Path: tmpDir, - Git: trgRepo, - }, - } - - checker := plan.FileUnalteredChecker{} - checker.Params.SourceRepo = plan.SourceRepo - checker.Params.TargetRepo = plan.TargetRepo - - err = checker.Check(hashPath, ctx) - assert.Nil(err) -} diff --git a/build/sync/plan/git/file_history.go b/build/sync/plan/git/file_history.go deleted file mode 100644 index db7a237..0000000 --- a/build/sync/plan/git/file_history.go +++ /dev/null @@ -1,111 +0,0 @@ -package git - -import ( - "crypto/sha1" //nolint - "encoding/hex" - "fmt" - "io" - "os" - "path/filepath" - - git "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing" - "github.com/go-git/go-git/v5/plumbing/object" - "github.com/pkg/errors" -) - -// ErrNotFound signifies the file was not found. -var ErrNotFound = fmt.Errorf("not found") - -// FileHistory will trace all the versions of a file in the git repository -// and return a list of sha1 hashes of that file. -func FileHistory(path string, repo *git.Repository) ([]string, error) { - logOpts := git.LogOptions{ - FileName: &path, - All: true, - } - commits, err := repo.Log(&logOpts) - if errors.Is(err, plumbing.ErrReferenceNotFound) { - return nil, ErrNotFound - } - if err != nil { - return nil, fmt.Errorf("failed to get commits for path %q: %v", path, err) - } - defer commits.Close() - hashHistory := []string{} - cerr := commits.ForEach(func(c *object.Commit) error { - root, err := repo.TreeObject(c.TreeHash) - if err != nil { - return fmt.Errorf("failed to get commit tree: %v", err) - } - f, err := traverseTree(root, path) - if err == object.ErrFileNotFound || err == object.ErrDirectoryNotFound { - // Ignoring file not found errors. - return nil - } else if err != nil { - return err - } - sum, err := getReaderHash(f) - f.Close() - if err != nil { - return err - } - hashHistory = append(hashHistory, sum) - return nil - }) - if cerr != nil && cerr != io.EOF { - return nil, cerr - } - if len(hashHistory) == 0 { - return nil, ErrNotFound - } - return hashHistory, nil -} - -func traverseTree(root *object.Tree, path string) (io.ReadCloser, error) { - dirName, fileName := filepath.Split(path) - var err error - t := root - if dirName != "" { - t, err = root.Tree(filepath.Clean(dirName)) - if err == object.ErrDirectoryNotFound { - return nil, err - } else if err != nil { - return nil, fmt.Errorf("failed to traverse tree to %q: %v", dirName, err) - } - } - f, err := t.File(fileName) - if err == object.ErrFileNotFound { - return nil, err - } else if err != nil { - return nil, fmt.Errorf("failed to lookup file %q: %v", fileName, err) - } - reader, err := f.Reader() - if err != nil { - return nil, fmt.Errorf("failed to open %q: %v", path, err) - } - return reader, nil -} - -func getReaderHash(r io.Reader) (string, error) { - h := sha1.New() // nolint - _, err := io.Copy(h, r) - if err != nil { - return "", err - } - return hex.EncodeToString(h.Sum(nil)), nil -} - -// GetFileHash calculates the sha1 hash sum of the file. -func GetFileHash(path string) (string, error) { - f, err := os.Open(path) - if err != nil { - return "", err - } - defer f.Close() - sum, err := getReaderHash(f) - if err != nil { - return "", err - } - return sum, nil -} diff --git a/build/sync/plan/git/file_history_test.go b/build/sync/plan/git/file_history_test.go deleted file mode 100644 index 6d82dec..0000000 --- a/build/sync/plan/git/file_history_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package git_test - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - "time" - - git "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/object" - "github.com/stretchr/testify/assert" - - gitutil "github.com/mattermost/mattermost-plugin-starter-template/build/sync/plan/git" -) - -var fileContents = []byte("abcdefg") - -func TestFileHistory(t *testing.T) { - assert := assert.New(t) - - dir, err := ioutil.TempDir("", "repo") - assert.Nil(err) - defer os.RemoveAll(dir) - - // Initialize a repository. - repo, err := git.PlainInit(dir, false) - assert.Nil(err) - w, err := repo.Worktree() - assert.Nil(err) - // Create repository files. - err = ioutil.WriteFile(filepath.Join(dir, "test"), fileContents, 0644) - assert.Nil(err) - _, err = w.Add("test") - assert.Nil(err) - sig := &object.Signature{ - Name: "test", - Email: "test@example.com", - When: time.Now(), - } - _, err = w.Commit("initial commit", &git.CommitOptions{Author: sig}) - assert.Nil(err) - pathA := "a.txt" - err = ioutil.WriteFile(filepath.Join(dir, pathA), fileContents, 0644) - assert.Nil(err) - pathB := "b.txt" - err = ioutil.WriteFile(filepath.Join(dir, pathB), fileContents, 0644) - assert.Nil(err) - _, err = w.Add(pathA) - assert.Nil(err) - _, err = w.Add(pathB) - assert.Nil(err) - _, err = w.Commit("add files", &git.CommitOptions{Author: sig}) - assert.Nil(err) - // Delete one of the files. - _, err = w.Remove(pathB) - assert.Nil(err) - _, err = w.Commit("remove file b.txt", &git.CommitOptions{ - Author: sig, - All: true, - }) - assert.Nil(err) - - repo, err = git.PlainOpen(dir) - assert.Nil(err) - - // Call file history on an existing file. - sums, err := gitutil.FileHistory("a.txt", repo) - assert.Nil(err) - assert.Equal([]string{"2fb5e13419fc89246865e7a324f476ec624e8740"}, sums) - - // Calling with a non-existent file returns error. - sums, err = gitutil.FileHistory(filepath.Join(dir, "nosuch_testfile.txt"), repo) - assert.Equal(gitutil.ErrNotFound, err) - assert.Nil(sums) - - // Calling with a non-existent file that was in git history returns no error. - _, err = gitutil.FileHistory(pathB, repo) - assert.Nil(err) -} diff --git a/build/sync/plan/plan.go b/build/sync/plan/plan.go deleted file mode 100644 index 794c775..0000000 --- a/build/sync/plan/plan.go +++ /dev/null @@ -1,245 +0,0 @@ -// Package plan handles the synchronization plan. -// -// Each synchronization plan is a set of checks and actions to perform on specified paths -// that will result in the "plugin" repository being updated. -package plan - -import ( - "encoding/json" - "fmt" - "os" - "sort" -) - -// Plan defines the plan for synchronizing a target and a source directory. -type Plan struct { - Checks []Check `json:"checks"` - // Each set of paths has multiple actions associated, each a fallback for the one - // previous to it. - Actions []ActionSet -} - -// UnmarshalJSON implements the `json.Unmarshaler` interface. -func (p *Plan) UnmarshalJSON(raw []byte) error { - var t jsonPlan - if err := json.Unmarshal(raw, &t); err != nil { - return err - } - p.Checks = make([]Check, len(t.Checks)) - for i, check := range t.Checks { - c, err := parseCheck(check.Type, check.Params) - if err != nil { - return fmt.Errorf("failed to parse check %q: %v", check.Type, err) - } - p.Checks[i] = c - } - - if len(t.Actions) > 0 { - p.Actions = make([]ActionSet, len(t.Actions)) - } - for i, actionSet := range t.Actions { - var err error - pathActions := make([]Action, len(actionSet.Actions)) - for i, action := range actionSet.Actions { - var actionConditions []Check - if len(action.Conditions) > 0 { - actionConditions = make([]Check, len(action.Conditions)) - } - for j, check := range action.Conditions { - actionConditions[j], err = parseCheck(check.Type, check.Params) - if err != nil { - return err - } - } - pathActions[i], err = parseAction(action.Type, action.Params, actionConditions) - if err != nil { - return err - } - } - p.Actions[i] = ActionSet{ - Paths: actionSet.Paths, - Actions: pathActions, - } - } - return nil -} - -// Execute executes the synchronization plan. -func (p *Plan) Execute(c Setup) error { - c.Logf("running pre-checks") - for _, check := range p.Checks { - err := check.Check("", c) // For pre-sync checks, the path is ignored. - if err != nil { - return fmt.Errorf("failed check: %v", err) - } - } - result := []pathResult{} - c.Logf("running actions") - for _, actions := range p.Actions { - PATHS_LOOP: - for _, path := range actions.Paths { - c.Logf("syncing path %q", path) - ACTIONS_LOOP: - for i, action := range actions.Actions { - c.Logf("running action for path %q", path) - err := action.Check(path, c) - if IsCheckFail(err) { - c.Logf("check failed, not running action: %v", err) - // If a check for an action fails, we switch to - // the next action associated with the path. - if i == len(actions.Actions)-1 { // no actions to fallback to. - c.Logf("path %q not handled - no more fallbacks", path) - result = append(result, - pathResult{ - Path: path, - Status: statusFailed, - Message: fmt.Sprintf("check failed, %s", err.Error()), - }) - } - continue ACTIONS_LOOP - } else if err != nil { - c.LogErrorf("unexpected error when running check: %v", err) - return fmt.Errorf("failed to run checks for action: %v", err) - } - err = action.Run(path, c) - if err != nil { - c.LogErrorf("action failed: %v", err) - return fmt.Errorf("action failed: %v", err) - } - c.Logf("path %q sync'ed successfully", path) - result = append(result, - pathResult{ - Path: path, - Status: statusUpdated, - }) - - continue PATHS_LOOP - } - } - } - - // Print execution result. - sort.SliceStable(result, func(i, j int) bool { return result[i].Path < result[j].Path }) - for _, res := range result { - if res.Message != "" { - fmt.Fprintf(os.Stdout, "%s\t%s: %s\n", res.Status, res.Path, res.Message) - } else { - fmt.Fprintf(os.Stdout, "%s\t%s\n", res.Status, res.Path) - } - } - return nil -} - -// Check returns an error if the condition fails. -type Check interface { - Check(string, Setup) error -} - -// ActionSet is a set of actions along with a set of paths to -// perform those actions on. -type ActionSet struct { - Paths []string - Actions []Action -} - -// Action runs the defined action. -type Action interface { - // Run performs the action on the specified path. - Run(string, Setup) error - // Check runs checks associated with the action - // before running it. - Check(string, Setup) error -} - -// jsonPlan is used to unmarshal Plan structures. -type jsonPlan struct { - Checks []struct { - Type string `json:"type"` - Params json.RawMessage `json:"params,omitempty"` - } - Actions []struct { - Paths []string `json:"paths"` - Actions []struct { - Type string `json:"type"` - Params json.RawMessage `json:"params,omitempty"` - Conditions []struct { - Type string `json:"type"` - Params json.RawMessage `json:"params"` - } - } - } -} - -func parseCheck(checkType string, rawParams json.RawMessage) (Check, error) { - var c Check - - var params interface{} - - switch checkType { - case "repo_is_clean": - tc := RepoIsCleanChecker{} - params = &tc.Params - c = &tc - case "exists": - tc := PathExistsChecker{} - params = &tc.Params - c = &tc - case "file_unaltered": - tc := FileUnalteredChecker{} - params = &tc.Params - c = &tc - default: - return nil, fmt.Errorf("unknown checker type %q", checkType) - } - - if len(rawParams) > 0 { - err := json.Unmarshal(rawParams, params) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal params for %s: %v", checkType, err) - } - } - return c, nil -} - -func parseAction(actionType string, rawParams json.RawMessage, checks []Check) (Action, error) { - var a Action - - var params interface{} - - switch actionType { - case "overwrite_file": - ta := OverwriteFileAction{} - ta.Conditions = checks - params = &ta.Params - a = &ta - case "overwrite_directory": - ta := OverwriteDirectoryAction{} - ta.Conditions = checks - params = &ta.Params - a = &ta - default: - return nil, fmt.Errorf("unknown action type %q", actionType) - } - - if len(rawParams) > 0 { - err := json.Unmarshal(rawParams, params) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal params for %s: %v", actionType, err) - } - } - return a, nil -} - -// pathResult contains the result of synchronizing a path. -type pathResult struct { - Path string - Status status - Message string -} - -type status string - -const ( - statusUpdated status = "UPDATED" - statusFailed status = "FAILED" -) diff --git a/build/sync/plan/plan_test.go b/build/sync/plan/plan_test.go deleted file mode 100644 index 711e150..0000000 --- a/build/sync/plan/plan_test.go +++ /dev/null @@ -1,253 +0,0 @@ -package plan_test - -import ( - "encoding/json" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/mattermost/mattermost-plugin-starter-template/build/sync/plan" -) - -func TestUnmarshalPlan(t *testing.T) { - assert := assert.New(t) - rawJSON := []byte(` -{ - "checks": [ - {"type": "repo_is_clean", "params": {"repo": "template"}} - ], - "actions": [ - { - "paths": ["abc"], - "actions": [{ - "type": "overwrite_file", - "params": {"create": true}, - "conditions": [{ - "type": "exists", - "params": {"repo": "plugin"} - }] - }] - } - ] -}`) - var p plan.Plan - err := json.Unmarshal(rawJSON, &p) - assert.Nil(err) - expectedCheck := plan.RepoIsCleanChecker{} - expectedCheck.Params.Repo = "template" - - expectedAction := plan.OverwriteFileAction{} - expectedAction.Params.Create = true - expectedActionCheck := plan.PathExistsChecker{} - expectedActionCheck.Params.Repo = "plugin" - expectedAction.Conditions = []plan.Check{&expectedActionCheck} - expected := plan.Plan{ - Checks: []plan.Check{&expectedCheck}, - Actions: []plan.ActionSet{{ - Paths: []string{"abc"}, - Actions: []plan.Action{ - &expectedAction, - }, - }}, - } - assert.Equal(expected, p) -} - -type mockCheck struct { - returnErr error - calledWith string // Path parameter the check was called with. -} - -// Check implements the plan.Check interface. -func (m *mockCheck) Check(path string, c plan.Setup) error { - m.calledWith = path - return m.returnErr -} - -type mockAction struct { - runErr error // Error to be returned by Run. - checkErr error // Error to be returned by Check. - calledWith string -} - -// Check implements plan.Action interface. -func (m *mockAction) Check(path string, c plan.Setup) error { - return m.checkErr -} - -// Run implements plan.Action interface. -func (m *mockAction) Run(path string, c plan.Setup) error { - m.calledWith = path - return m.runErr -} - -// TestRunPlanSuccessfully tests a successful execution of a sync plan. -func TestRunPlanSuccessfully(t *testing.T) { - assert := assert.New(t) - - setup := plan.Setup{} // mocked actions and checks won't be accessing the setup. - - preCheck := &mockCheck{} - action1 := &mockAction{} - action2 := &mockAction{} - - p := &plan.Plan{ - Checks: []plan.Check{preCheck}, - Actions: []plan.ActionSet{{ - Paths: []string{"somepath"}, - Actions: []plan.Action{ - action1, - action2, - }, - }}, - } - err := p.Execute(setup) - assert.Nil(err) - - assert.Equal("", preCheck.calledWith) - assert.Equal("somepath", action1.calledWith) - assert.Equal("", action2.calledWith) // second action was not called. -} - -// TestRunPlanPreCheckFail checks the scenario where a sync plan precheck -// fails, aborting the whole operation. -func TestRunPlanPreCheckFail(t *testing.T) { - assert := assert.New(t) - - setup := plan.Setup{} // mocked actions and checks won't be accessing the setup. - - preCheck := &mockCheck{returnErr: plan.CheckFailf("check failed")} - action1 := &mockAction{} - action2 := &mockAction{} - - p := &plan.Plan{ - Checks: []plan.Check{preCheck}, - Actions: []plan.ActionSet{{ - Paths: []string{"somepath"}, - Actions: []plan.Action{ - action1, - action2, - }, - }}, - } - err := p.Execute(setup) - assert.EqualError(err, "failed check: check failed") - - assert.Equal("", preCheck.calledWith) - // None of the actions were executed. - assert.Equal("", action1.calledWith) - assert.Equal("", action2.calledWith) -} - -// TestRunPlanActionCheckFails tests the situation where an action's -// check returns a recoverable error, forcing the plan to execute the fallback action. -func TestRunPlanActionCheckFails(t *testing.T) { - assert := assert.New(t) - - setup := plan.Setup{} // mocked actions and checks won't be accessing the setup. - - action1 := &mockAction{checkErr: plan.CheckFailf("action check failed")} - action2 := &mockAction{} - - p := &plan.Plan{ - Actions: []plan.ActionSet{{ - Paths: []string{"somepath"}, - Actions: []plan.Action{ - action1, - action2, - }, - }}, - } - err := p.Execute(setup) - assert.Nil(err) - - assert.Equal("", action1.calledWith) // First action was not run. - assert.Equal("somepath", action2.calledWith) // Second action was run. -} - -// TestRunPlanNoFallbacks tests the case where an action's check fails, -// but there are not more fallback actions for that path. -func TestRunPlanNoFallbacks(t *testing.T) { - assert := assert.New(t) - - setup := plan.Setup{} // mocked actions and checks won't be accessing the setup. - - action1 := &mockAction{checkErr: plan.CheckFailf("fail")} - action2 := &mockAction{checkErr: plan.CheckFailf("fail")} - - p := &plan.Plan{ - Actions: []plan.ActionSet{{ - Paths: []string{"somepath"}, - Actions: []plan.Action{ - action1, - action2, - }, - }}, - } - err := p.Execute(setup) - assert.Nil(err) - - // both actions were not executed. - assert.Equal("", action1.calledWith) - assert.Equal("", action2.calledWith) -} - -// TestRunPlanCheckError tests the scenario where a plan check fails with -// an unexpected error. Plan execution is aborted. -func TestRunPlanCheckError(t *testing.T) { - assert := assert.New(t) - - setup := plan.Setup{} // mocked actions and checks won't be accessing the setup. - - preCheck := &mockCheck{returnErr: fmt.Errorf("fail")} - action1 := &mockAction{} - action2 := &mockAction{} - - p := &plan.Plan{ - Checks: []plan.Check{preCheck}, - Actions: []plan.ActionSet{{ - Paths: []string{"somepath"}, - Actions: []plan.Action{ - action1, - action2, - }, - }}, - } - err := p.Execute(setup) - assert.EqualError(err, "failed check: fail") - - assert.Equal("", preCheck.calledWith) - // Actions were not run. - assert.Equal("", action1.calledWith) - assert.Equal("", action2.calledWith) -} - -// TestRunPlanActionError tests the scenario where an action fails, -// aborting the whole sync process. -func TestRunPlanActionError(t *testing.T) { - assert := assert.New(t) - - setup := plan.Setup{} // mocked actions and checks won't be accessing the setup. - - preCheck := &mockCheck{} - action1 := &mockAction{runErr: fmt.Errorf("fail")} - action2 := &mockAction{} - - p := &plan.Plan{ - Checks: []plan.Check{preCheck}, - Actions: []plan.ActionSet{{ - Paths: []string{"somepath"}, - Actions: []plan.Action{ - action1, - action2, - }, - }}, - } - err := p.Execute(setup) - assert.EqualError(err, "action failed: fail") - - assert.Equal("", preCheck.calledWith) - assert.Equal("somepath", action1.calledWith) - assert.Equal("", action2.calledWith) // second action was not called. -} diff --git a/build/sync/plan/setup.go b/build/sync/plan/setup.go deleted file mode 100644 index a9a779d..0000000 --- a/build/sync/plan/setup.go +++ /dev/null @@ -1,80 +0,0 @@ -package plan - -import ( - "fmt" - "os" - "path/filepath" - - git "github.com/go-git/go-git/v5" -) - -// RepoID identifies a repository - either plugin or template. -type RepoID string - -const ( - // SourceRepo is the id of the template repository (source). - SourceRepo RepoID = "source" - // TargetRepo is the id of the plugin repository (target). - TargetRepo RepoID = "target" -) - -// Setup contains information about both parties -// in the sync: the plugin repository being updated -// and the source of the update - the template repo. -type Setup struct { - Source RepoSetup - Target RepoSetup - VerboseLogging bool -} - -// Logf logs the provided message. -// If verbose output is not enabled, the message will not be printed. -func (c Setup) Logf(tpl string, args ...interface{}) { - if c.VerboseLogging { - fmt.Fprintf(os.Stderr, tpl+"\n", args...) - } -} - -// LogErrorf logs the provided error message. -func (c Setup) LogErrorf(tpl string, args ...interface{}) { - fmt.Fprintf(os.Stderr, tpl+"\n", args...) -} - -// GetRepo is a helper to get the required repo setup. -// If the target parameter is not one of "plugin" or "template", -// the function panics. -func (c Setup) GetRepo(r RepoID) RepoSetup { - switch r { - case TargetRepo: - return c.Target - case SourceRepo: - return c.Source - default: - panic(fmt.Sprintf("cannot get repository setup %q", r)) - } -} - -// PathInRepo returns the full path of a file in the specified repository. -func (c Setup) PathInRepo(repo RepoID, path string) string { - r := c.GetRepo(repo) - return filepath.Join(r.Path, path) -} - -// RepoSetup contains relevant information -// about a single repository (either source or target). -type RepoSetup struct { - Git *git.Repository - Path string -} - -// GetRepoSetup returns the repository setup for the specified path. -func GetRepoSetup(path string) (RepoSetup, error) { - repo, err := git.PlainOpen(path) - if err != nil { - return RepoSetup{}, fmt.Errorf("failed to access git repository at %q: %v", path, err) - } - return RepoSetup{ - Git: repo, - Path: path, - }, nil -} diff --git a/build/sync/plan/testdata/a b/build/sync/plan/testdata/a deleted file mode 100644 index 2e65efe..0000000 --- a/build/sync/plan/testdata/a +++ /dev/null @@ -1 +0,0 @@ -a \ No newline at end of file diff --git a/build/sync/plan/testdata/b/c b/build/sync/plan/testdata/b/c deleted file mode 100644 index 3410062..0000000 --- a/build/sync/plan/testdata/b/c +++ /dev/null @@ -1 +0,0 @@ -c \ No newline at end of file