diff --git a/Makefile b/Makefile index 370ccb0d433df..ed4f4a3dec136 100644 --- a/Makefile +++ b/Makefile @@ -45,8 +45,8 @@ check-static: tools/bin/golangci-lint tools/bin/golangci-lint run -v $$($(PACKAGE_DIRECTORIES)) unconvert:tools/bin/unconvert - @echo "unconvert check" - @GO111MODULE=on tools/bin/unconvert ./... + @echo "unconvert check(skip check the genenrated or copied code in lightning)" + @GO111MODULE=on tools/bin/unconvert $(UNCONVERT_PACKAGES) gogenerate: @echo "go generate ./..." @@ -81,7 +81,9 @@ test: test_part_1 test_part_2 test_part_1: checklist explaintest -test_part_2: gotest gogenerate +test_part_2: gotest gogenerate br_unit_test + +test_part_br: br_unit_test br_integration_test explaintest: server_check @cd cmd/explaintest && ./run-tests.sh -s ../../bin/tidb-server @@ -103,7 +105,7 @@ ifeq ("$(TRAVIS_COVERAGE)", "1") @export log_level=info; \ $(OVERALLS) -project=github.com/pingcap/tidb \ -covermode=count \ - -ignore='.git,vendor,cmd,docs,tests,LICENSES' \ + -ignore='.git,br,vendor,cmd,docs,tests,LICENSES' \ -concurrency=4 \ -- -coverpkg=./... \ || { $(FAILPOINT_DISABLE); exit 1; } @@ -204,6 +206,10 @@ tools/bin/errdoc-gen: tools/check/go.mod tools/bin/golangci-lint: curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b ./tools/bin v1.41.1 +tools/bin/vfsgendev: tools/check/go.mod + cd tools/check; \ + $(GO) build -o ../bin/vfsgendev github.com/shurcooL/vfsgen/cmd/vfsgendev + # Usage: # # $ make vectorized-bench VB_FILE=Time VB_FUNC=builtinCurrentDateSig @@ -230,3 +236,97 @@ endif bench-daily: cd ./session && \ go test -run TestBenchDaily --date `git log -n1 --date=unix --pretty=format:%cd` --commit `git log -n1 --pretty=format:%h` --outfile $(TO) + +build_tools: build_br build_lightning build_lightning-ctl + +br_web: + @cd br/web && npm install && npm run build + +build_br: + CGO_ENABLED=1 $(GOBUILD) $(RACE_FLAG) -ldflags '$(TOOL_LDFLAGS) $(CHECK_FLAG)' -o $(BR_BIN) br/cmd/br/*.go + +build_lightning_for_web: + CGO_ENABLED=1 $(GOBUILD) -tags dev $(RACE_FLAG) -ldflags '$(TOOL_LDFLAGS) $(CHECK_FLAG)' -o $(LIGHTNING_BIN) br/cmd/tidb-lightning/main.go + +build_lightning: + CGO_ENABLED=1 $(GOBUILD) $(RACE_FLAG) -ldflags '$(TOOL_LDFLAGS) $(CHECK_FLAG)' -o $(LIGHTNING_BIN) br/cmd/tidb-lightning/main.go + +build_lightning-ctl: + CGO_ENABLED=1 $(GOBUILD) $(RACE_FLAG) -ldflags '$(TOOL_LDFLAGS) $(CHECK_FLAG)' -o $(LIGHTNING_CTL_BIN) br/cmd/tidb-lightning-ctl/main.go + +build_for_br_integration_test: + @make failpoint-enable + ($(GOTEST) -c -cover -covermode=count \ + -coverpkg=github.com/pingcap/tidb/br/... \ + -o $(BR_BIN).test \ + github.com/pingcap/tidb/br/cmd/br && \ + $(GOTEST) -c -cover -covermode=count \ + -coverpkg=github.com/pingcap/tidb/br/... \ + -o $(LIGHTNING_BIN).test \ + github.com/pingcap/tidb/br/cmd/tidb-lightning && \ + $(GOTEST) -c -cover -covermode=count \ + -coverpkg=github.com/pingcap/tidb/br/... \ + -o $(LIGHTNING_CTL_BIN).test \ + github.com/pingcap/tidb/br/cmd/tidb-lightning-ctl && \ + $(GOBUILD) $(RACE_FLAG) -o bin/locker br/tests/br_key_locked/*.go && \ + $(GOBUILD) $(RACE_FLAG) -o bin/gc br/tests/br_z_gc_safepoint/*.go && \ + $(GOBUILD) $(RACE_FLAG) -o bin/oauth br/tests/br_gcs/*.go && \ + $(GOBUILD) $(RACE_FLAG) -o bin/rawkv br/tests/br_rawkv/*.go && \ + $(GOBUILD) $(RACE_FLAG) -o bin/parquet_gen br/tests/lightning_checkpoint_parquet/*.go \ + ) || (make failpoint-disable && exit 1) + @make failpoint-disable + +br_unit_test: export ARGS=$$($(BR_PACKAGES)) +br_unit_test: + @make failpoint-enable + $(GOTEST) $(RACE_FLAG) -tags leak $(ARGS) || ( make failpoint-disable && exit 1 ) + @make failpoint-disable + +br_integration_test: br_bins build_br build_for_br_integration_test + @cd br && tests/run.sh + +br_compatibility_test_prepare: + @cd br && tests/run_compatible.sh prepare + +br_compatibility_test: + @cd br && tests/run_compatible.sh run + +# There is no FreeBSD environment for GitHub actions. So cross-compile on Linux +# but that doesn't work with CGO_ENABLED=1, so disable cgo. The reason to have +# cgo enabled on regular builds is performance. +ifeq ("$(GOOS)", "freebsd") + GOBUILD = CGO_ENABLED=0 GO111MODULE=on go build -trimpath -ldflags '$(LDFLAGS)' +endif + +br_coverage: + tools/bin/gocovmerge "$(TEST_DIR)"/cov.* | grep -vE ".*.pb.go|.*__failpoint_binding__.go" > "$(TEST_DIR)/all_cov.out" +ifeq ("$(JenkinsCI)", "1") + tools/bin/goveralls -coverprofile=$(TEST_DIR)/all_cov.out -service=jenkins-ci -repotoken $(COVERALLS_TOKEN) +else + go tool cover -html "$(TEST_DIR)/all_cov.out" -o "$(TEST_DIR)/all_cov.html" + grep -F ' $@ + @rm tmp_parser.go + +data_parsers: tools/bin/vfsgendev br/pkg/lightning/mydump/parser_generated.go br_web + PATH="$(GOPATH)/bin":"$(PATH)":"$(TOOLS)" protoc -I. -I"$(GOPATH)/src" br/pkg/lightning/checkpoints/checkpointspb/file_checkpoints.proto --gogofaster_out=. + tools/bin/vfsgendev -source='"github.com/pingcap/tidb/br/pkg/lightning/web".Res' && mv res_vfsdata.go br/pkg/lightning/web/ diff --git a/Makefile.common b/Makefile.common index 8885dc6c3a979..255c89b432979 100644 --- a/Makefile.common +++ b/Makefile.common @@ -43,10 +43,12 @@ endif ARCH := "`uname -s`" LINUX := "Linux" MAC := "Darwin" -PACKAGE_LIST := go list ./...| grep -vE "cmd|github.com\/pingcap\/tidb\/tests" +PACKAGE_LIST := go list ./...| grep -vE "cmd|github.com\/pingcap\/tidb\/tests|github.com\/pingcap\/tidb\/br" PACKAGES ?= $$($(PACKAGE_LIST)) PACKAGE_DIRECTORIES := $(PACKAGE_LIST) | sed 's|github.com/pingcap/$(PROJECT)/||' FILES := $$(find $$($(PACKAGE_DIRECTORIES)) -name "*.go") +UNCONVERT_PACKAGES_LIST := go list ./...| grep -vE "lightning\/checkpoints|lightning\/manual|lightning\/common" +UNCONVERT_PACKAGES := $$($(UNCONVERT_PACKAGES_LIST)) FAILPOINT_ENABLE := $$(find $$PWD/ -type d | grep -vE "(\.git|tools)" | xargs tools/bin/failpoint-ctl enable) FAILPOINT_DISABLE := $$(find $$PWD/ -type d | grep -vE "(\.git|tools)" | xargs tools/bin/failpoint-ctl disable) @@ -78,3 +80,31 @@ CHECK_FLAG = ifeq ("$(WITH_CHECK)", "1") CHECK_FLAG = $(TEST_LDFLAGS) endif + +BR_PKG := github.com/pingcap/tidb/br +BR_PACKAGES := go list ./...| grep "github.com\/pingcap\/tidb\/br" +LIGHTNING_BIN := bin/tidb-lightning +LIGHTNING_CTL_BIN := bin/tidb-lightning-ctl +BR_BIN := bin/br +TEST_DIR := /tmp/backup_restore_test + +TOOL_RELEASE_VERSION = +ifeq ($(TOOL_RELEASE_VERSION),) + TOOL_RELEASE_VERSION := v4.0.0-dev + release_version_regex := ^v4\..*$$ + release_branch_regex := "^release-[0-9]\.[0-9].*$$|^HEAD$$|^.*/*tags/v[0-9]\.[0-9]\..*$$" + ifneq ($(shell git rev-parse --abbrev-ref HEAD | egrep $(release_branch_regex)),) + # If we are in release branch, try to use tag version. + ifneq ($(shell git describe --tags --dirty | egrep $(release_version_regex)),) + TOOL_RELEASE_VERSION := $(shell git describe --tags --dirty) + endif + else ifneq ($(shell git status --porcelain),) + # Add -dirty if the working tree is dirty for non release branch. + TOOL_RELEASE_VERSION := $(TOOL_RELEASE_VERSION)-dirty + endif +endif + +TOOL_LDFLAGS += -X "$(BR_PKG)/pkg/version/build.ReleaseVersion=$(TOOL_RELEASE_VERSION)" +TOOL_LDFLAGS += -X "$(BR_PKG)/pkg/version/build.BuildTS=$(shell date -u '+%Y-%m-%d %I:%M:%S')" +TOOL_LDFLAGS += -X "$(BR_PKG)/pkg/version/build.GitHash=$(shell git rev-parse HEAD)" +TOOL_LDFLAGS += -X "$(BR_PKG)/pkg/version/build.GitBranch=$(shell git rev-parse --abbrev-ref HEAD)" diff --git a/bindinfo/bind_test.go b/bindinfo/bind_test.go index 0ddcf3c9741f5..8be8eb4ce5b9d 100644 --- a/bindinfo/bind_test.go +++ b/bindinfo/bind_test.go @@ -2039,10 +2039,9 @@ func (s *testSuite) TestExplainShowBindSQL(c *C) { "select * from `test` . `t` SELECT * FROM `test`.`t` USE INDEX (`a`)", )) - tk.MustExec("explain select * from t") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Using the bindSQL: SELECT * FROM `test`.`t` USE INDEX (`a`)")) - tk.MustExec("explain analyze select * from t") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Using the bindSQL: SELECT * FROM `test`.`t` USE INDEX (`a`)")) + tk.MustExec("explain format = 'verbose' select * from t") + tk.MustQuery("show warnings").Check(testkit.Rows("Note 1105 Using the bindSQL: SELECT * FROM `test`.`t` USE INDEX (`a`)")) + // explain analyze do not support verbose yet. } func (s *testSuite) TestDMLIndexHintBind(c *C) { diff --git a/bindinfo/handle.go b/bindinfo/handle.go index 36a54deccd003..1301f6e2556ac 100644 --- a/bindinfo/handle.go +++ b/bindinfo/handle.go @@ -699,7 +699,13 @@ func getHintsForSQL(sctx sessionctx.Context, sql string) (string, error) { rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(context.TODO(), fmt.Sprintf("EXPLAIN FORMAT='hint' %s", sql)) sctx.GetSessionVars().UsePlanBaselines = origVals if rs != nil { - defer terror.Call(rs.Close) + defer func() { + // Audit log is collected in Close(), set InRestrictedSQL to avoid 'create sql binding' been recorded as 'explain'. + origin := sctx.GetSessionVars().InRestrictedSQL + sctx.GetSessionVars().InRestrictedSQL = true + terror.Call(rs.Close) + sctx.GetSessionVars().InRestrictedSQL = origin + }() } if err != nil { return "", err diff --git a/br/.codecov.yml b/br/.codecov.yml new file mode 100644 index 0000000000000..9fc8064faee5c --- /dev/null +++ b/br/.codecov.yml @@ -0,0 +1,22 @@ +codecov: + require_ci_to_pass: yes + +comment: + layout: "reach, diff, flags, files" + behavior: default + require_changes: false # if true: only post the comment if coverage changes + require_base: no # [yes :: must have a base report to post] + require_head: yes # [yes :: must have a head report to post] + branches: # branch names that can post comment + - "master" + +coverage: + status: + project: + default: + # Allow the coverage to drop by 3% + target: 85% + threshold: 3% + branches: + - master + patch: off diff --git a/br/.dockerignore b/br/.dockerignore new file mode 120000 index 0000000000000..3e4e48b0b5fe6 --- /dev/null +++ b/br/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/br/.editorconfig b/br/.editorconfig new file mode 100644 index 0000000000000..43c6a002cce45 --- /dev/null +++ b/br/.editorconfig @@ -0,0 +1,10 @@ +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +# tab_size = 4 spaces +[*.go] +indent_style = tab +indent_size = 4 +trim_trailing_whitespace = true diff --git a/br/.gitattributes b/br/.gitattributes new file mode 100644 index 0000000000000..ba35fa1000ef6 --- /dev/null +++ b/br/.gitattributes @@ -0,0 +1 @@ +*_generated.go linguist-generated=true diff --git a/br/.github/ISSUE_TEMPLATE/bug-report.md b/br/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000000000..555efb841bd26 --- /dev/null +++ b/br/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,45 @@ +--- +name: "πŸ› Bug Report" +about: Something isn't working as expected +title: '' +labels: 'type/bug ' +--- + +Please answer these questions before submitting your issue. Thanks! + +1. What did you do? +If possible, provide a recipe for reproducing the error. + + +2. What did you expect to see? + + + +3. What did you see instead? + + + +4. What version of BR and TiDB/TiKV/PD are you using? + + + +5. Operation logs + - Please upload `br.log` for BR if possible + - Please upload `tidb-lightning.log` for TiDB-Lightning if possible + - Please upload `tikv-importer.log` from TiKV-Importer if possible + - Other interesting logs + + +6. Configuration of the cluster and the task + - `tidb-lightning.toml` for TiDB-Lightning if possible + - `tikv-importer.toml` for TiKV-Importer if possible + - `topology.yml` if deployed by TiUP + + +7. Screenshot/exported-PDF of Grafana dashboard or metrics' graph in Prometheus if possible diff --git a/br/.github/ISSUE_TEMPLATE/feature-request.md b/br/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000000000..ed6b4c5b0bf7c --- /dev/null +++ b/br/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,19 @@ +--- +name: "πŸš€ Feature Request" +about: I have a suggestion +labels: 'type/feature-request' +--- + +## Feature Request + +### Describe your feature request related problem: + + +### Describe the feature you'd like: + + +### Describe alternatives you've considered: + + +### Teachability, Documentation, Adoption, Migration Strategy: + diff --git a/br/.github/ISSUE_TEMPLATE/question.md b/br/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000000000..23a8118377288 --- /dev/null +++ b/br/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,24 @@ +--- +name: "\U0001F914 Question" +labels: "type/question" +about: Usage question that isn't answered in docs or discussion + +--- + +## Question + + + diff --git a/br/.github/challenge-bot.yml b/br/.github/challenge-bot.yml new file mode 100644 index 0000000000000..15d2f38ece965 --- /dev/null +++ b/br/.github/challenge-bot.yml @@ -0,0 +1 @@ +defaultSigLabel: sig/migrate diff --git a/br/.github/pull_request_template.md b/br/.github/pull_request_template.md new file mode 100644 index 0000000000000..7f64aa17286f2 --- /dev/null +++ b/br/.github/pull_request_template.md @@ -0,0 +1,42 @@ + + +### What problem does this PR solve? + + +### What is changed and how it works? + + +### Check List + +Tests + + - Unit test + - Integration test + - Manual test (add detailed scripts or steps below) + - No code + +Code changes + + - Has exported function/method change + - Has exported variable/fields change + - Has interface methods change + - Has persistent data change + +Side effects + + - Possible performance regression + - Increased code complexity + - Breaking backward compatibility + +Related changes + + - Need to cherry-pick to the release branch + - Need to update the documentation + +### Release note + + - + + diff --git a/br/.github/workflows/build.yml b/br/.github/workflows/build.yml new file mode 100644 index 0000000000000..472def46ae2a0 --- /dev/null +++ b/br/.github/workflows/build.yml @@ -0,0 +1,68 @@ +name: build +on: + push: + branches: + - master + - 'release-[0-9].[0-9]*' + paths-ignore: + - '**.html' + - '**.md' + - 'CNAME' + - 'LICENSE' + - 'docs/**' + - 'tests/**' + - 'docker/**' + - '.github/workflows/**.yml' + pull_request: + branches: + - master + - 'release-[0-9].[0-9]*' + paths-ignore: + - '**.html' + - '**.md' + - 'CNAME' + - 'LICENSE' + - 'docs/**' + - 'tests/**' + - 'docker/**' + - '.github/workflows/**.yml' + +jobs: + compile: + name: ${{ matrix.os }} / ${{ matrix.target}} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: macos-latest + target: x86_64-apple-darwin + + - os: ubuntu-latest + target: aarch64-unknown-linux-gnu + + - os: windows-latest + target: x86_64-pc-windows-msvc + steps: + - uses: actions/checkout@v2.1.0 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.16 + + - name: Run build + run: make build + + compile-freebsd: + name: Compile for FreeBSD job + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.1.0 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.16 + + - name: Compile for FreeBSD + run: GOOS=freebsd make build diff --git a/br/.github/workflows/compatible_test.yml b/br/.github/workflows/compatible_test.yml new file mode 100644 index 0000000000000..9fbf0c52e83ac --- /dev/null +++ b/br/.github/workflows/compatible_test.yml @@ -0,0 +1,71 @@ +name: compatibility-test + +on: + push: + branches: + - master + - 'release-[0-9].[0-9]*' + paths-ignore: + - '**.html' + - '**.md' + - 'CNAME' + - 'LICENSE' + - 'docs/**' + - 'tests/**' + - 'docker/**' + - '.github/workflows/**.yml' + pull_request: + branches: + - master + - 'release-[0-9].[0-9]*' + paths-ignore: + - '**.html' + - '**.md' + - 'CNAME' + - 'LICENSE' + - 'docs/**' + - 'tests/**' + - 'docker/**' + - '.github/workflows/**.yml' + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 25 + steps: + + - name: Free disk space + run: | + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet + docker volume prune -f + docker image prune -f + + - uses: actions/checkout@v2 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.16 + + - name: Generate compatibility test backup data + timeout-minutes: 15 + run: sh compatibility/prepare_backup.sh + + - name: Start server + run: | + TAG=nightly PORT_SUFFIX=1 docker-compose -f compatibility/backup_cluster.yaml rm -s -v + TAG=nightly PORT_SUFFIX=1 docker-compose -f compatibility/backup_cluster.yaml build + TAG=nightly PORT_SUFFIX=1 docker-compose -f compatibility/backup_cluster.yaml up --remove-orphans -d + TAG=nightly PORT_SUFFIX=1 docker-compose -f compatibility/backup_cluster.yaml exec -T control make compatibility_test + + - name: Collect component log + if: ${{ failure() }} + run: | + tar czvf ${{ github.workspace }}/logs.tar.gz /tmp/br/docker/backup_logs/* + + - uses: actions/upload-artifact@v2 + if: ${{ failure() }} + with: + name: logs + path: ${{ github.workspace }}/logs.tar.gz diff --git a/br/.gitignore b/br/.gitignore new file mode 100644 index 0000000000000..c29d04732ce40 --- /dev/null +++ b/br/.gitignore @@ -0,0 +1,14 @@ +/br +/bin +/.idea +/docker/data/ +/docker/logs/ +*.swp +.DS_Store +/go.mod +/go.sum + +# for the web interface +web/node_modules/ +web/dist/ +.vscode/ diff --git a/br/.golangci.yml b/br/.golangci.yml new file mode 100644 index 0000000000000..0cb2a9b651251 --- /dev/null +++ b/br/.golangci.yml @@ -0,0 +1,12 @@ +linters-settings: + gocyclo: + min-complexity: 40 + +issues: + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + # TODO Remove it. + - path: split_client.go + text: "SA1019:" + linters: + - staticcheck diff --git a/br/COMPATIBILITY_TEST.md b/br/COMPATIBILITY_TEST.md new file mode 100644 index 0000000000000..b5580835baee8 --- /dev/null +++ b/br/COMPATIBILITY_TEST.md @@ -0,0 +1,42 @@ +# Compatibility test + +## Background + +We had some incompatibility issues in the past, which made BR cannot restore backed up data in some situations. +So we need a test workflow to check the compatiblity. + +## Goal + +- Ensure backward compatibility for restoring data from the previous 3 minor versions + +## Workflow + +### Data Preparation + +This workflow needs previous backup data. To get this data. we perform the following steps + +- Run a TiDB cluster with previous version. +- Run backup jobs with corresponding BR version, with different storages (s3, gcs). + +Given we test for the previous 3 versions, and there are 2 different storage systems, we will produce 6 backup archives for 6 separate compatibility tests. + +### Test Content + +- Start TiDB cluster with nightly version. +- Build BR binary with current directory. +- Use BR to restore different version backup data one by one. +- Make sure restore data is expected. + +### Running tests + +Start a cluster with docker-compose and Build br with latest version. + +```sh +docker-compose -f docker-compose.yaml rm -s -v && \ +docker-compose -f docker-compose.yaml build && \ +docker-compose -f docker-compose.yaml up --remove-orphans +``` + +```sh +docker-compose -f docker-compose.yaml control make compatibility_test +``` diff --git a/br/CONTRIBUTING.md b/br/CONTRIBUTING.md new file mode 100644 index 0000000000000..1f2846471a7a1 --- /dev/null +++ b/br/CONTRIBUTING.md @@ -0,0 +1,90 @@ +# How to contribute + +This document outlines some of the conventions on development workflow, commit +message formatting, contact points and other resources to make it easier to get +your contribution accepted. + +## Getting started + +- Fork the repository on GitHub. +- Read the README.md for build instructions. +- Play with the project, submit bugs, submit patches! + +## Building BR + +Developing BR requires: + +* [Go 1.16+](http://golang.org/doc/code.html) +* An internet connection to download the dependencies + +Simply run `make` to build the program. + +```sh +make +``` + +### Running tests + +This project contains unit tests and integration tests with coverage collection. +See [tests/README.md](./tests/README.md) for how to execute and add tests. + +### Updating dependencies + +BR uses [Go 1.11 module](https://github.com/golang/go/wiki/Modules) to manage dependencies. +To add or update a dependency: use the `go mod edit` command to change the dependency. + +## Contribution flow + +This is a rough outline of what a contributor's workflow looks like: + +- Create a topic branch from where you want to base your work. This is usually `master`. +- Make commits of logical units and add test case if the change fixes a bug or adds new functionality. +- Run tests and make sure all the tests are passed. +- Make sure your commit messages are in the proper format (see below). +- Push your changes to a topic branch in your fork of the repository. +- Submit a pull request. +- Your PR must receive LGTMs from two maintainers. + +Thanks for your contributions! + +### Code style + +The coding style suggested by the Golang community is used in BR. +See the [style doc](https://github.com/golang/go/wiki/CodeReviewComments) for details. + +Please follow this style to make BR easy to review, maintain and develop. + +### Format of the Commit Message + +We follow a rough convention for commit messages that is designed to answer two +questions: what changed and why. The subject line should feature the what and +the body of the commit should describe the why. + +``` +restore: add comment for variable declaration + +Improve documentation. +``` + +The format can be described more formally as follows: + +``` +: + + + +