Remove build/sync tool (#186)

This commit is contained in:
Ben Schumacher 2023-08-02 12:45:06 +02:00 committed by GitHub
parent 8862f96d1f
commit d1047c7140
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 0 additions and 2134 deletions

View file

@ -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

View file

@ -1,55 +0,0 @@
module github.com/mattermost/mattermost-plugin-starter-template/build
go 1.19
require (
github.com/go-git/go-git/v5 v5.4.2
github.com/mattermost/mattermost/server/public v0.0.6
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.8.4
sigs.k8s.io/yaml v1.3.0
)
require (
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/blang/semver v3.5.1+incompatible // 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.12.0 // 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.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
github.com/mattermost/logr/v2 v2.0.16 // indirect
github.com/mitchellh/go-homedir v1.1.0 // 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/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/sergi/go-diff v1.1.0 // 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.0 // indirect
golang.org/x/crypto v0.10.0 // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/text v0.10.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
)

View file

@ -1,339 +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=
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.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
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 v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
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.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-asn1-ber/asn1-ber v1.3.2-0.20191121212151-29be175fc3a3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
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.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
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/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.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
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/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a h1:i0+Se9S+2zL5CBxJouqn2Ej6UQMwH1c57ZB6DVnqck4=
github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os=
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/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
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/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
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 v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
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/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
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-20201202150706-ee0e6284187d h1:/RJ/UV7M5c7L2TQ0KNm4yZxxFvC1nvRz/gY/Daa35aI=
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d/go.mod h1:HLbgMEI5K131jpxGazJ97AxfPDt31osq36YS1oxFQPQ=
github.com/mattermost/logr/v2 v2.0.16 h1:jnePX4cPskC3WDFvUardh/xZfxNdsFXbEERJQ1kUEDE=
github.com/mattermost/logr/v2 v2.0.16/go.mod h1:1dm/YhTpozsqANXxo5Pi5zYLBsal2xY0pX+JZNbzYJY=
github.com/mattermost/mattermost/server/public v0.0.6 h1:FUaJ+P36E3Tt12Umdm8p1h7sZNUeObDk3p3aFTaBkCo=
github.com/mattermost/mattermost/server/public v0.0.6/go.mod h1:Y7Ht1haGGrsuYzX73HhpSe2VnbGLuZj2/tsQslHd2/M=
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/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
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/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.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
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/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.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.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI=
go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs=
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-20190219172222-a4c6cb3142f2/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-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
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/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-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
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.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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-20200227125254-8fa46927fb4f/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.3.0/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.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
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=

View file

@ -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.

View file

@ -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 <plan.yml> <plugin_directory>\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
}

View file

@ -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

View file

@ -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
}

View file

@ -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())
}
}
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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"
)

View file

@ -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.
}

View file

@ -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
}

View file

@ -1 +0,0 @@
a

View file

@ -1 +0,0 @@
c