├── zh-CN ├── README.md ├── test_case.md ├── declaration.md ├── import_packages.md ├── project_structure.md ├── copyright.md ├── commentary.md ├── naming_rules.md └── coding_guidelines.md ├── LICENSE ├── README.md └── en-US.md /zh-CN/README.md: -------------------------------------------------------------------------------- 1 | # 规范索引 2 | 3 | - [版权声明](copyright.md) 4 | - [项目结构](project_structure.md) 5 | - [导入标准库、第三方或其它包](import_packages.md) 6 | - [注释规范](commentary.md) 7 | - [命名规则](naming_rules.md) 8 | - [声明语句](declaration.md) 9 | - [代码指导](coding_guidelines.md) 10 | - [测试用例](test_case.md) -------------------------------------------------------------------------------- /zh-CN/test_case.md: -------------------------------------------------------------------------------- 1 | # 测试用例 2 | 3 | - 单元测试都必须使用 [GoConvey](http://goconvey.co/) 编写,且辅助包覆盖率必须在 80% 以上。 4 | 5 | ### 使用示例 6 | 7 | - 为辅助包书写使用示例的时,文件名均命名为 `example_test.go`。 8 | - 测试用例的函数名称必须以 `Test_` 开头,例如:`Test_Logger`。 9 | - 如果为方法书写测试用例,则需要以 `Text__` 的形式命名,例如:`Test_Macaron_Run`。 -------------------------------------------------------------------------------- /zh-CN/declaration.md: -------------------------------------------------------------------------------- 1 | # 声明语句 2 | 3 | ### 函数或方法 4 | 5 | 函数或方法的参数排列顺序遵循以下几点原则(从左到右): 6 | 7 | 1. 参数的重要程度与逻辑顺序 8 | 2. 简单类型优先于复杂类型 9 | 3. 尽可能将同种类型的参数放在相邻位置,则只需写一次类型 10 | 11 | #### 示例 12 | 13 | 以下声明语句,`User` 类型要复杂于 `string` 类型,但由于 `Repository` 是 `User` 的附属品,首先确定 `User` 才能继而确定 `Repository`。因此,`User` 的顺序要优先于 `repoName`。 14 | 15 | ```Go 16 | func IsRepositoryExist(user *User, repoName string) (bool, error) { ... 17 | ``` 18 | -------------------------------------------------------------------------------- /zh-CN/import_packages.md: -------------------------------------------------------------------------------- 1 | # 导入标准库、第三方或其它包 2 | 3 | 除标准库外,Go 语言的导入路径基本上依赖代码托管平台上的 URL 路径,因此一个源文件需要导入的包有 4 种分类:标准库、第三方包、组织内其它包和当前包的子包。 4 | 5 | 基本规则: 6 | 7 | - 如果同时存在 2 种及以上,则需要使用分组来导入。每个分类使用一个分组,采用空行作为分区之间的分割。 8 | - 在非测试文件(`*_test.go`)中,禁止使用 `.` 来简化导入包的对象调用。 9 | - 禁止使用相对路径导入(`./subpackage`),所有导入路径必须符合 `go get` 标准。 10 | 11 | 下面是一个完整的示例: 12 | 13 | ```Go 14 | import ( 15 | "fmt" 16 | "html/template" 17 | "net/http" 18 | "os" 19 | 20 | "github.com/codegangsta/cli" 21 | "gopkg.in/macaron.v1" 22 | 23 | "github.com/gogits/git" 24 | "github.com/gogits/gfm" 25 | 26 | "github.com/gogits/gogs/routers" 27 | "github.com/gogits/gogs/routers/repo" 28 | "github.com/gogits/gogs/routers/user" 29 | ) 30 | ``` 31 | -------------------------------------------------------------------------------- /zh-CN/project_structure.md: -------------------------------------------------------------------------------- 1 | # 项目结构 2 | 3 | 以下为一般项目结构,根据不同的 Web 框架习惯,可使用括号内的文字替换;根据不同的项目类型和需求,可自由增删某些结构: 4 | 5 | ``` 6 | - templates (views) # 模板文件 7 | - public (static) # 静态文件 8 | - css 9 | - fonts 10 | - img 11 | - js 12 | - routes # 路由逻辑处理 13 | - models # 数据逻辑层 14 | - pkg # 子模块 15 | - setting # 应用配置存取 16 | - cmd # 命令行程序命令 17 | - conf # 默认配置 18 | - locale # i18n 本地化文件 19 | - custom # 自定义配置 20 | - data # 应用生成数据文件 21 | - log # 应用生成日志文件 22 | ``` 23 | 24 | ## 命令行应用 25 | 26 | 当应用类型为命令行应用时,需要将命令相关文件存放于 `/cmd` 目录下,并为每个命令创建一个单独的源文件: 27 | 28 | ``` 29 | /cmd 30 | dump.go 31 | fix.go 32 | serve.go 33 | update.go 34 | web.go 35 | ``` 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Code Convention 2 | 3 | This is a 100% opinionated and paranoid code convention for the Go Programming Language by [@unknwon](https://github.com/unknwon). It may or may not be compatible with [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments), or any other guidelines out there. That's fine, that is neither mine nor your problem. 4 | 5 | ![](https://imgs.xkcd.com/comics/standards.png) 6 | 7 | Be sure to always remember, "尽信书不如无书". 8 | 9 | For non-Chinese readers, [what does 尽信书不如无书 mean?](https://chinese.stackexchange.com/a/26717) 10 | 11 |
12 | 13 | Ready? OK, pick one you like: 14 | 15 | - [English](en-US.md) 16 | 17 | - [简体中文](zh-CN/README.md) _Stay tuned!_ 18 | 19 | ## Roadmap for current version 20 | 21 | - [x] Overhaul exsiting content for en-US 22 | - [ ] Add more pages about: 23 | - [x] Linting 24 | - [ ] Database layer construction and testing 25 | - [ ] Error wrapping 26 | - [ ] Dependency injection 27 | - [ ] TBD 28 | - [ ] Re-translated everything from en-US to zh-CN 29 | 30 | ## Notes about v1 31 | 32 | The [v1](https://github.com/unknwon/go-code-convention/tree/v1) was originally drafted back in 2015, and majority of the content hasn't been updated since. As of 2021, after another 6 years working with Go, some of aspects already feel outdated for me, especially I've learned more practices by working with amazing people. I would love to have these practices documented so I can reference back. 33 | 34 | ## License 35 | 36 | This project is under the Unlicense License. See the [LICENSE](LICENSE) file for the full license text. 37 | 38 | -------------------------------------------------------------------------------- /zh-CN/copyright.md: -------------------------------------------------------------------------------- 1 | # 版权声明 2 | 3 | 作为开源项目,必须有相应的开源许可证才能算是真正的开源。在选择了一个开源许可证之后,需要在源文件中进行相应的版权声明才能生效。以下分别以 [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 和 MIT 授权许可为例。 4 | 5 | ### Apache License, Version 2.0 6 | 7 | 该许可证要求在所有的源文件中的头部放置以下内容才能算协议对该文件有效: 8 | 9 | ``` 10 | // Copyright [yyyy] [name of copyright owner] 11 | // 12 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 13 | // not use this file except in compliance with the License. You may obtain 14 | // a copy of the License at 15 | // 16 | // http://www.apache.org/licenses/LICENSE-2.0 17 | // 18 | // Unless required by applicable law or agreed to in writing, software 19 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 20 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 21 | // License for the specific language governing permissions and limitations 22 | // under the License. 23 | ``` 24 | 25 | 其中,`[yyyy]` 表示该源文件创建的年份。紧随其后的是 `[name of copyright owner]`,即版权所有者。如果为个人项目,就写个人名称;若为团队项目,则宜写团队名称。 26 | 27 | ### MIT License 28 | 29 | 一般使用 MIT 授权的项目,需在源文件头部增加以下内容: 30 | 31 | ``` 32 | // Copyright [yyyy] [name of copyright owner]. All rights reserved. 33 | // Use of this source code is governed by a MIT-style 34 | // license that can be found in the LICENSE file. 35 | ``` 36 | 37 | 其中,年份和版权所有者的名称填写规则与 Apache License, Version 2.0 的一样。 38 | 39 | ### 其它说明 40 | 41 | - 其它类型的开源许可证基本上都可参照以上两种方案。 42 | - 如果存在不同作者或组织对同个源文件的修改,在协议兼容的情况下,可将首行变为多行,按照先后次序排放: 43 | 44 | ``` 45 | // Copyright 2011 Gary Burd 46 | // Copyright 2013 Unknwon 47 | ``` 48 | 49 | - 在 README 文件最后中需要说明项目所采用的开源许可证: 50 | 51 | ``` 52 | ## 授权许可 53 | 54 | 本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](LICENSE) 文件中。 55 | ``` 56 | -------------------------------------------------------------------------------- /zh-CN/commentary.md: -------------------------------------------------------------------------------- 1 | # 注释规范 2 | 3 | - 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 4 | - 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 5 | - 包、函数、方法和类型的注释说明都是一个完整的句子。 6 | - 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 7 | - 注释的单行长度不能超过 80 个字符。 8 | 9 | ### 包级别 10 | 11 | - 包级别的注释就是对包的介绍,只需在同个包的任一源文件中说明即可有效。 12 | - 对于 `main` 包,一般只有一行简短的注释用以说明包的用途,且以项目名称开头: 13 | 14 | ```Go 15 | // Gogs (Go Git Service) is a painless self-hosted Git Service. 16 | package main 17 | ``` 18 | 19 | - 对于一个复杂项目的子包,一般情况下不需要包级别注释,除非是代表某个特定功能的模块。 20 | - 对于简单的非 `main` 包,也可用一行注释概括。 21 | - 对于相对功能复杂的非 `main` 包,一般都会增加一些使用示例或基本说明,且以 `Package ` 开头: 22 | 23 | ```Go 24 | /* 25 | Package regexp implements a simple library for regular expressions. 26 | 27 | The syntax of the regular expressions accepted is: 28 | 29 | regexp: 30 | concatenation { '|' concatenation } 31 | concatenation: 32 | { closure } 33 | closure: 34 | term [ '*' | '+' | '?' ] 35 | term: 36 | '^' 37 | '$' 38 | '.' 39 | character 40 | '[' [ '^' ] character-ranges ']' 41 | '(' regexp ')' 42 | */ 43 | package regexp 44 | ``` 45 | 46 | - 特别复杂的包说明,可单独创建 [`doc.go`](https://github.com/robfig/cron/blob/master/doc.go) 文件来加以说明。 47 | 48 | ### 结构、接口及其它类型 49 | 50 | - 类型的定义一般都以单数形式描述: 51 | 52 | ```Go 53 | // Request represents a request to run a command. 54 | type Request struct { ... 55 | ``` 56 | 57 | - 如果为接口,则一般以以下形式描述: 58 | 59 | ```Go 60 | // FileInfo is the interface that describes a file and is returned by Stat and Lstat. 61 | type FileInfo interface { ... 62 | ``` 63 | 64 | 65 | ### 函数与方法 66 | 67 | - 函数与方法的注释需以函数或方法的名称作为开头: 68 | 69 | ```Go 70 | // Post returns *BeegoHttpRequest with POST method. 71 | ``` 72 | 73 | - 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: 74 | 75 | ```Go 76 | // Copy copies file from source to target path. 77 | // It returns false and error when error occurs in underlying function calls. 78 | ``` 79 | 80 | - 若函数或方法为判断类型(返回值主要为 `bool` 类型),则以 ` returns true if` 开头: 81 | 82 | ```Go 83 | // HasPrefix returns true if name has any string in given slice as prefix. 84 | func HasPrefix(name string, prefixes []string) bool { ... 85 | ``` 86 | 87 | ### 其它说明 88 | 89 | 90 | - 当某个部分等待完成时,可用 `TODO:` 开头的注释来提醒维护人员。 91 | - 当某个部分存在已知问题进行需要修复或改进时,可用 `FIXME:` 开头的注释来提醒维护人员。 92 | - 当需要特别说明某个问题时,可用 `NOTE:` 开头的注释: 93 | 94 | ```Go 95 | // NOTE: os.Chmod and os.Chtimes don't recognize symbolic link, 96 | // which will lead "no such file or directory" error. 97 | return os.Symlink(target, dest) 98 | ``` 99 | -------------------------------------------------------------------------------- /zh-CN/naming_rules.md: -------------------------------------------------------------------------------- 1 | # 命名规则 2 | 3 | ### 文件名 4 | 5 | - 整个应用或包的主入口文件应当是 `main.go` 或与应用名称简写相同。例如:`Gogs` 的主入口文件名为 `gogs.go`。 6 | 7 | ### 函数或方法 8 | 9 | - 若函数或方法为判断类型(返回值主要为 `bool` 类型),则名称应以 `Has`, `Is`, `Can` 或 `Allow` 等判断性动词开头: 10 | 11 | ```go 12 | func HasPrefix(name string, prefixes []string) bool { ... } 13 | func IsEntry(name string, entries []string) bool { ... } 14 | func CanManage(name string) bool { ... } 15 | func AllowGitHook() bool { ... } 16 | ``` 17 | 18 | ### 常量 19 | 20 | - 常量均需使用全部大写字母组成,并使用下划线分词: 21 | 22 | ```go 23 | const APP_VER = "0.7.0.1110 Beta" 24 | ``` 25 | 26 | - 如果是枚举类型的常量,需要先创建相应类型: 27 | 28 | ```go 29 | type Scheme string 30 | 31 | const ( 32 | HTTP Scheme = "http" 33 | HTTPS Scheme = "https" 34 | ) 35 | ``` 36 | 37 | - 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: 38 | 39 | ```go 40 | type PullRequestStatus int 41 | 42 | const ( 43 | PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota 44 | PULL_REQUEST_STATUS_CHECKING 45 | PULL_REQUEST_STATUS_MERGEABLE 46 | ) 47 | ``` 48 | 49 | ### 变量 50 | 51 | - 变量命名基本上遵循相应的英文表达或简写。 52 | - 在相对简单的环境(对象数量少、针对性强)中,可以将一些名称由完整单词简写为单个字母,例如: 53 | - `user` 可以简写为 `u` 54 | - `userID` 可以简写 `uid` 55 | - 若变量类型为 `bool` 类型,则名称应以 `Has`, `Is`, `Can` 或 `Allow` 开头: 56 | 57 | ```go 58 | var isExist bool 59 | var hasConflict bool 60 | var canManage bool 61 | var allowGitHook bool 62 | ``` 63 | 64 | - 上条规则也适用于结构定义: 65 | 66 | ```go 67 | // Webhook represents a web hook object. 68 | type Webhook struct { 69 | ID int64 `xorm:"pk autoincr"` 70 | RepoID int64 71 | OrgID int64 72 | URL string `xorm:"url TEXT"` 73 | ContentType HookContentType 74 | Secret string `xorm:"TEXT"` 75 | Events string `xorm:"TEXT"` 76 | *HookEvent `xorm:"-"` 77 | IsSSL bool `xorm:"is_ssl"` 78 | IsActive bool 79 | HookTaskType HookTaskType 80 | Meta string `xorm:"TEXT"` // store hook-specific attributes 81 | LastStatus HookStatus // Last delivery status 82 | Created time.Time `xorm:"CREATED"` 83 | Updated time.Time `xorm:"UPDATED"` 84 | } 85 | ``` 86 | 87 | #### 变量命名惯例 88 | 89 | 变量名称一般遵循驼峰法,但遇到特有名词时,需要遵循以下规则: 90 | 91 | - 如果变量为私有,且特有名词为首个单词,则使用小写,如 `apiClient`。 92 | - 其它情况都应当使用该名词原有的写法,如 `APIClient`、`repoID`、`UserID`。 93 | 94 | 下面列举了一些常见的特有名词: 95 | 96 | ```go 97 | // A GonicMapper that contains a list of common initialisms taken from golang/lint 98 | var LintGonicMapper = GonicMapper{ 99 | "API": true, 100 | "ASCII": true, 101 | "CPU": true, 102 | "CSS": true, 103 | "DNS": true, 104 | "EOF": true, 105 | "GUID": true, 106 | "HTML": true, 107 | "HTTP": true, 108 | "HTTPS": true, 109 | "ID": true, 110 | "IP": true, 111 | "JSON": true, 112 | "LHS": true, 113 | "QPS": true, 114 | "RAM": true, 115 | "RHS": true, 116 | "RPC": true, 117 | "SLA": true, 118 | "SMTP": true, 119 | "SSH": true, 120 | "TLS": true, 121 | "TTL": true, 122 | "UI": true, 123 | "UID": true, 124 | "UUID": true, 125 | "URI": true, 126 | "URL": true, 127 | "UTF8": true, 128 | "VM": true, 129 | "XML": true, 130 | "XSRF": true, 131 | "XSS": true, 132 | } 133 | ``` 134 | -------------------------------------------------------------------------------- /zh-CN/coding_guidelines.md: -------------------------------------------------------------------------------- 1 | # 代码指导 2 | 3 | ### 基本约束 4 | 5 | - 所有应用的 `main` 包需要有 `APP_VER` 常量表示版本,格式为 `X.Y.Z.Date [Status]`,例如:`0.7.6.1112 Beta`。 6 | - 单独的库需要有函数 `Version` 返回库版本号的字符串,格式为 `X.Y.Z[.Date]`。 7 | - 当单行代码超过 80 个字符时,就要考虑分行。分行的规则是以参数为单位将从较长的参数开始换行,以此类推直到每行长度合适: 8 | 9 | ```go 10 | So(z.ExtractTo( 11 | path.Join(os.TempDir(), "testdata/test2"), 12 | "dir/", "dir/bar", "readonly"), ShouldBeNil) 13 | ``` 14 | 15 | - 当单行声明语句超过 80 个字符时,就要考虑分行。分行的规则是将参数按类型分组,紧接着的声明语句的是一个空行,以便和函数体区别: 16 | 17 | ```go 18 | // NewNode initializes and returns a new Node representation. 19 | func NewNode( 20 | importPath, downloadUrl string, 21 | tp RevisionType, val string, 22 | isGetDeps bool) *Node { 23 | 24 | n := &Node{ 25 | Pkg: Pkg{ 26 | ImportPath: importPath, 27 | RootPath: GetRootPath(importPath), 28 | Type: tp, 29 | Value: val, 30 | }, 31 | DownloadURL: downloadUrl, 32 | IsGetDeps: isGetDeps, 33 | } 34 | n.InstallPath = path.Join(setting.InstallRepoPath, n.RootPath) + n.ValSuffix() 35 | return n 36 | } 37 | ``` 38 | 39 | - 分组声明一般需要按照功能来区分,而不是将所有类型都分在一组: 40 | 41 | ```go 42 | const ( 43 | // Default section name. 44 | DEFAULT_SECTION = "DEFAULT" 45 | // Maximum allowed depth when recursively substituing variable names. 46 | _DEPTH_VALUES = 200 47 | ) 48 | 49 | type ParseError int 50 | 51 | const ( 52 | ERR_SECTION_NOT_FOUND ParseError = iota + 1 53 | ERR_KEY_NOT_FOUND 54 | ERR_BLANK_SECTION_NAME 55 | ERR_COULD_NOT_PARSE 56 | ) 57 | ``` 58 | 59 | - 当一个源文件中存在多个相对独立的部分时,为方便区分,需使用由 [ASCII Generator](http://www.network-science.de/ascii/) 提供的句型字符标注(示例:`Comment`): 60 | 61 | ```go 62 | // _________ __ 63 | // \_ ___ \ ____ _____ _____ ____ _____/ |_ 64 | // / \ \/ / _ \ / \ / \_/ __ \ / \ __\ 65 | // \ \___( <_> ) Y Y \ Y Y \ ___/| | \ | 66 | // \______ /\____/|__|_| /__|_| /\___ >___| /__| 67 | // \/ \/ \/ \/ \/ 68 | ``` 69 | 70 | - 函数或方法的顺序一般需要按照依赖关系由浅入深由上至下排序,即最底层的函数出现在最前面。例如,下方的代码,函数 `ExecCmdDirBytes` 属于最底层的函数,它被 `ExecCmdDir` 函数调用,而 `ExecCmdDir` 又被 `ExecCmd` 调用: 71 | 72 | ```go 73 | // ExecCmdDirBytes executes system command in given directory 74 | // and return stdout, stderr in bytes type, along with possible error. 75 | func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) { 76 | ... 77 | } 78 | 79 | // ExecCmdDir executes system command in given directory 80 | // and return stdout, stderr in string type, along with possible error. 81 | func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) { 82 | bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...) 83 | return string(bufOut), string(bufErr), err 84 | } 85 | 86 | // ExecCmd executes system command 87 | // and return stdout, stderr in string type, along with possible error. 88 | func ExecCmd(cmdName string, args ...string) (string, string, error) { 89 | return ExecCmdDir("", cmdName, args...) 90 | } 91 | ``` 92 | 93 | - 结构附带的方法应置于结构定义之后,按照所对应操作的字段顺序摆放方法: 94 | 95 | ```go 96 | type Webhook struct { ... } 97 | func (w *Webhook) GetEvent() { ... } 98 | func (w *Webhook) SaveEvent() error { ... } 99 | func (w *Webhook) HasPushEvent() bool { ... } 100 | ``` 101 | 102 | - 如果一个结构拥有对应操作函数,大体上按照 `CRUD` 的顺序放置结构定义之后: 103 | 104 | ```go 105 | func CreateWebhook(w *Webhook) error { ... } 106 | func GetWebhookById(hookId int64) (*Webhook, error) { ... } 107 | func UpdateWebhook(w *Webhook) error { ... } 108 | func DeleteWebhook(hookId int64) error { ... } 109 | ``` 110 | 111 | - 如果一个结构拥有以 `Has`、`Is`、`Can` 或 `Allow` 开头的函数或方法,则应将它们至于所有其它函数及方法之前;这些函数或方法以 `Has`、`Is`、`Can`、`Allow` 的顺序排序。 112 | - 变量的定义要放置在相关函数之前: 113 | 114 | ```go 115 | var CmdDump = cli.Command{ 116 | Name: "dump", 117 | ... 118 | Action: runDump, 119 | Flags: []cli.Flag{}, 120 | } 121 | 122 | func runDump(*cli.Context) { ... 123 | ``` 124 | 125 | - 在初始化结构时,尽可能使用一一对应方式: 126 | 127 | ```go 128 | AddHookTask(&HookTask{ 129 | Type: HTT_WEBHOOK, 130 | Url: w.Url, 131 | Payload: p, 132 | ContentType: w.ContentType, 133 | IsSsl: w.IsSsl, 134 | }) 135 | ``` 136 | -------------------------------------------------------------------------------- /en-US.md: -------------------------------------------------------------------------------- 1 | # Go Code Convention 2 | 3 | ## Copyright 4 | 5 | For any open source project, there must be a LICENSE file in the repository root to claim the rights. 6 | 7 | Here are two examples of using [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) and MIT License. 8 | 9 | ### Apache License, Version 2.0 10 | 11 | This license requires to put following content at the beginning of every file: 12 | 13 | ``` 14 | // Copyright [yyyy] [name of copyright owner] 15 | // 16 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 17 | // not use this file except in compliance with the License. You may obtain 18 | // a copy of the License at 19 | // 20 | // http://www.apache.org/licenses/LICENSE-2.0 21 | // 22 | // Unless required by applicable law or agreed to in writing, software 23 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 24 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 25 | // License for the specific language governing permissions and limitations 26 | // under the License. 27 | ``` 28 | 29 | Replace `[yyyy]` with the creation year of the file. Then use personal name for personal projects, or organization name for team projects to replace `[name of copyright owner]`. 30 | 31 | ### MIT License 32 | 33 | This license requires to put following content at the beginning of every file: 34 | 35 | ``` 36 | // Copyright [yyyy] [name of copyright owner]. All rights reserved. 37 | // Use of this source code is governed by a MIT-style 38 | // license that can be found in the LICENSE file. 39 | ``` 40 | 41 | Replace `[yyyy]` with the creation year of the file. 42 | 43 | ### Other notes 44 | 45 | - Other types of license can follow the template of above two examples. 46 | 47 | - If a file has been modified by different individuals and/or organizations, and multiple licenses are compatiable, then change the first line to multiple lines and sort them in the order of time: 48 | 49 | ``` 50 | // Copyright 2011 Gary Burd 51 | // Copyright 2013 Unknwon 52 | ``` 53 | 54 | - Spefify which license is used for the project in bottom of the README file: 55 | 56 | ``` 57 | ## License 58 | 59 | This project is under the MIT License. See the [LICENSE](LICENSE) file for the full license text. 60 | ``` 61 | 62 | ## Project structure 63 | 64 | For server-side services and CLI applications, they generally follow the same style of project structure as follows: 65 | 66 | ``` 67 | .bin/ # The directory to put all locally compiled binaries, should be ignored by .gitignore 68 | .github/ # GitHub specific configuration 69 | assets/ # Static resources, including those to be embeded into binaries 70 | migrations/ # Files for database schema migrations 71 | templates/ # Template files for rendering HTML pages 72 | embed.go # The main file to tell Go toolchain to embed resources 73 | cmd/ # All "main" packages which produce binaries should be placed under this directory 74 | / 75 | main.go # The entry point of a main package 76 | data/ # Application generated data 77 | dev/ # Scripts for enhancing quality-of-life of development 78 | docs/ # Project specific documentation 79 | internal/ # Common packages, "internal" is also a keyword to prevent other projects to import in Go 80 | conf/ # Read and/or write application configuration 81 | db/ # Database layer 82 | route/ # Routing layer 83 | log/ # Appication generated logs 84 | .golangci.yml # Custom configuration for golangci-lint 85 | ``` 86 | 87 | ### Example `embed.go` 88 | 89 | The following shows an example of embeding database schema migrations using Go 1.16 [embed](https://blog.carlmjohnson.net/post/2021/how-to-use-go-embed/): 90 | 91 | ```go 92 | package assets 93 | 94 | import ( 95 | "embed" 96 | ) 97 | 98 | //go:embed migrations/*.sql 99 | var Migrations embed.FS 100 | ``` 101 | 102 | ## Import packages 103 | 104 | All package import paths must be valid Go Modules path except packages from standard library. 105 | 106 | Generally, there are four types of packages a source file could import: 107 | 108 | 1. Packages from standard library 109 | 2. Third-party packages 110 | 3. Packages in the same organization but not in the same repository 111 | 4. Packages in the same repository 112 | 113 | Basic rules: 114 | 115 | - Use different groups to separate import paths by an blank line for two or more types of packages. 116 | - Must not use `.` to simplify import path except in test files (`*_test.go`). 117 | - Must not use relative path (`./subpackage`) as import path. 118 | 119 | Here is a concrete example: 120 | 121 | ```go 122 | import ( 123 | "fmt" 124 | "html/template" 125 | "net/http" 126 | "os" 127 | 128 | "github.com/urfave/cli" 129 | "gopkg.in/macaron.v1" 130 | 131 | "github.com/gogs/git-module" 132 | 133 | "github.com/gogs/gogs/internal/route" 134 | "github.com/gogs/gogs/internal/route/repo" 135 | "github.com/gogs/gogs/internal/route/user" 136 | ) 137 | ``` 138 | 139 | ## Commentary 140 | 141 | - All exported objects must be well-commented; internal objects can be commented when needed. 142 | - If the object is countable and does not know the number of it, use singular form and present tense; otherwise, use plural form. 143 | - Comment of package, function, method and type must be a complete sentence (i.e. capitalize the first letter and end with a period). 144 | - Maximum length of a comment line should be 80 characters. 145 | 146 | ### Package 147 | 148 | - Only one file is needed for package-level comment. 149 | 150 | - For `main` packages, a line of introduction is enough, and should start with the binary name: 151 | 152 | ```Go 153 | // Gogs is a painless self-hosted Git Service. 154 | package main 155 | ``` 156 | 157 | - For sub-packages of a complex project, no package level comment is required unless it's a functional module. 158 | 159 | - For simple non-`main` packages, a line of introduction is enough as well. 160 | 161 | - For more complex functional non-`main` packages, comment should have some basic usage examples and must start with `Package `: 162 | 163 | ```Go 164 | /* 165 | Package regexp implements a simple library for regular expressions. 166 | 167 | The syntax of the regular expressions accepted is: 168 | 169 | regexp: 170 | concatenation { '|' concatenation } 171 | concatenation: 172 | { closure } 173 | closure: 174 | term [ '*' | '+' | '?' ] 175 | term: 176 | '^' 177 | '$' 178 | '.' 179 | character 180 | '[' [ '^' ] character-ranges ']' 181 | '(' regexp ')' 182 | */ 183 | package regexp 184 | ``` 185 | 186 | - When few paragraphs cannot explain the package well, you should create a [`doc.go`](https://github.com/robfig/cron/blob/master/doc.go) file for it. 187 | 188 | ### Struct and interface 189 | 190 | - Type is often described in singular form, and the comment should state what it actuall does: 191 | 192 | ```Go 193 | // Request represents a request to run a command. 194 | type Request struct { ... 195 | ``` 196 | 197 | - Interface should be described as follows, and the comment should state what implementations should be doing: 198 | 199 | ```Go 200 | // FileInfo is the interface that describes a file and is returned by Stat and Lstat. 201 | type FileInfo interface { ... 202 | ``` 203 | 204 | 205 | ### Functions and methods 206 | 207 | - Comments of functions and methods must start with its name: 208 | 209 | ```Go 210 | // Post returns *BeegoHttpRequest with POST method. 211 | ``` 212 | 213 | - If one sentence is not enough, go to next line: 214 | 215 | ```Go 216 | // Copy copies file from source to target path. 217 | // It returns false and error when error occurs in underlying function calls. 218 | ``` 219 | 220 | - If the main purpose of a function or method is returning a `bool` value, its comment should start with ` returns true if`: 221 | 222 | ```Go 223 | // HasPrefix returns true if name has any string in given slice as prefix. 224 | func HasPrefix(name string, prefixes []string) bool { ... 225 | ``` 226 | 227 | ### Other notes 228 | 229 | - When something is waiting to be done, use comment starts with `TODO:` to remind maintainers. 230 | 231 | - When a known problem/issue/bug needs to be fixed/improved, use comment starts with `FIXME:` to remind maintainers. 232 | 233 | - When something is too magic and needs to be explained, use comment starts with `NOTE:`: 234 | 235 | ```Go 236 | // NOTE: os.Chmod and os.Chtimes don't recognize symbolic link, 237 | // which will lead "no such file or directory" error. 238 | return os.Symlink(target, dest) 239 | ``` 240 | 241 | - When dealing with security-related logic, use comment starts with `SECURITY:` 242 | - When something important but is easy to overlook, use comment starts with `WARNING:` 243 | 244 | - When the object is deprecated, use comment starts with `DEPRECATED:`: 245 | 246 | ```go 247 | // Email returns the user's oldest email, if one exists. 248 | // Deprecated: use Emails instead. 249 | func (r *UserResolver) Email(ctx context.Context) (string, error) { 250 | ``` 251 | 252 | 253 | ## Naming rules 254 | 255 | ### Directories 256 | 257 | - Do not use underscores (`_`) in directory names, use hyphens (`-`) instead. 258 | 259 | ### Files 260 | 261 | - Do not use hyphens (`-`) in file names, use underscores (`_`) instead. 262 | - The file contains the entry point of the application or package should be named as `main.go` or same as the package. 263 | 264 | ### Functions and methods 265 | 266 | - If the main purpose of the function or method is returning a `bool` type value, the name of function or method should starts with `Has`, `Is`, `Can` or `Allow`, etc. 267 | 268 | ```go 269 | func HasPrefix(name string, prefixes []string) bool { ... } 270 | func IsEntry(name string, entries []string) bool { ... } 271 | func CanManage(name string) bool { ... } 272 | func AllowGitHook() bool { ... } 273 | ``` 274 | 275 | ### Constants 276 | 277 | - Constant should use camel cases except for coined terms and brand names (see later): 278 | 279 | ```go 280 | const appVersion = "0.13.0+dev" 281 | ``` 282 | 283 | - If you need enumerated type, you should define the corresponding type first: 284 | 285 | ```go 286 | type Scheme string 287 | 288 | const ( 289 | HTTP Scheme = "http" 290 | HTTPS Scheme = "https" 291 | ) 292 | ``` 293 | 294 | - If functionality of the module is relatively complicated and easy to mixed up with constant name, you can add prefix to every constant for the enumerated type: 295 | 296 | ```go 297 | type PullRequestStatus int 298 | 299 | const ( 300 | PullRequestStatusConflict PullRequestStatus = iota 301 | PullRequestStatusChecking 302 | PullRequestStatusMergable 303 | ) 304 | ``` 305 | 306 | ### Variables 307 | 308 | - A variable name should follow general English expression or shorthand. 309 | 310 | - In relatively simple (less objects and more specific) context, variable name can use simplified form as follows: 311 | 312 | - `userID` to `uid` 313 | - `repository` to `repo` 314 | 315 | - If variable type is `bool`, its name should start with `Has`, `Is`, `Can` or `Allow`, etc. 316 | 317 | ```go 318 | var isExist bool 319 | var hasConflict bool 320 | var canManage bool 321 | var allowGitHook bool 322 | ``` 323 | 324 | - Same rules also apply for defining structs: 325 | 326 | ```go 327 | // Webhook represents a web hook object. 328 | type Webhook struct { 329 | ID int64 330 | RepoID int64 331 | OrgID int64 332 | URL string 333 | ContentType HookContentType 334 | Secret string 335 | Events string 336 | *HookEvent 337 | IsSSL bool 338 | IsActive bool 339 | HookTaskType HookTaskType 340 | Meta string 341 | LastStatus HookStatus 342 | CreatedAt time.Time 343 | UpdatedAt time.Time 344 | } 345 | ``` 346 | 347 | ### Coined terms and brand names 348 | 349 | When you encounter coined terms and brand names, naming should respect the original or the most widely accepted form for the letter case. For example, use "GitHub" not "Github", use "API" not "Api", use "ID" not "Id". 350 | 351 | When the coined term and brand name is the first word in a variable or constant name, keep them having the same case. For example, `apiClient`, `SSLCertificate`, `githubClient`. 352 | 353 | Here is a list of words which are commonly identified as coined terms: 354 | 355 | ```go 356 | // A GonicMapper that contains a list of common initialisms taken from golang/lint 357 | var LintGonicMapper = GonicMapper{ 358 | "API": true, 359 | "ASCII": true, 360 | "CPU": true, 361 | "CSS": true, 362 | "DNS": true, 363 | "EOF": true, 364 | "GUID": true, 365 | "HTML": true, 366 | "HTTP": true, 367 | "HTTPS": true, 368 | "ID": true, 369 | "IP": true, 370 | "JSON": true, 371 | "LHS": true, 372 | "QPS": true, 373 | "RAM": true, 374 | "RHS": true, 375 | "RPC": true, 376 | "SLA": true, 377 | "SMTP": true, 378 | "SSH": true, 379 | "TLS": true, 380 | "TTL": true, 381 | "UI": true, 382 | "UID": true, 383 | "UUID": true, 384 | "URI": true, 385 | "URL": true, 386 | "UTF8": true, 387 | "VM": true, 388 | "XML": true, 389 | "XSRF": true, 390 | "XSS": true, 391 | } 392 | ``` 393 | 394 | ## Declarations 395 | 396 | ### Functions or methods 397 | 398 | Order of arguments of functions or methods should generally apply following rules (from left to right): 399 | 400 | 1. More important to less important. 401 | 2. Simpler types to more complicated types. 402 | 3. Same types should be put together whenever possible. 403 | 404 | #### Example 405 | 406 | In the following declaration, type `User` is more complicated than type `string`, but `Repository` belongs to `User` (which makes the `user` argument more important), so `user` is more left than `repoName`: 407 | 408 | ```Go 409 | func IsRepositoryExist(user *User, repoName string) (bool, error) { ... 410 | ``` 411 | 412 | ## Coding guidelines 413 | 414 | - Do not initialize structs with unnamed fields and do break fields into multiple lines: 415 | 416 | ```go 417 | func main() { 418 | ... 419 | awsCloudwatchLogsLogGroup := os.Getenv("AWS_CLOUDWATCH_LOGS_LOG_GROUP") 420 | if awsCloudwatchLogsLogGroup != "" { 421 | client := awsutil.NewDefaultClient() 422 | err := log.New( 423 | "cloudwatchlogs", 424 | awsutil.CloudWatchLogsIniter(), 425 | 1000, 426 | awsutil.CloudWatchLogsConfig{ // <---- Focus here 427 | Level: log.LevelInfo, 428 | Client: client.NewCloudWatchLogsClient(), 429 | LogGroupName: awsCloudwatchLogsLogGroup, 430 | LogStreamPrefix: awsCloudwatchLogsLogGroup + "-stream", 431 | Retention: 30, 432 | MaxRetries: 3, 433 | }, 434 | ) 435 | if err != nil { 436 | log.Fatal("Failed to init cloudwatchlogs logger: %v", err) 437 | } 438 | } 439 | ... 440 | } 441 | ``` 442 | 443 | - Group declaration should be organized by types, not all together: 444 | 445 | ```go 446 | const ( 447 | // Default section name. 448 | DefaultSection = "DEFAULT" 449 | // Maximum allowed depth when recursively substituing variable names. 450 | depthValues = 200 451 | ) 452 | 453 | type ParseError int 454 | 455 | const ( 456 | ErrSectionNotFound ParseError = iota + 1 457 | ErrKeyNotFound 458 | ErrBlankSectionName 459 | ErrCouldNotParse 460 | ) 461 | ``` 462 | 463 | - Functions or methods are ordered by the dependency relationship, such that the most dependent function or method should be at the top. In the following example, `ExecCmdDirBytes` is the most fundamental function, it's called by `ExecCmdDir`, and `ExecCmdDir` is also called by `ExecCmd`: 464 | 465 | ```go 466 | // ExecCmdDirBytes executes system command in given directory 467 | // and return stdout, stderr in bytes type, along with possible error. 468 | func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) { 469 | ... 470 | } 471 | 472 | // ExecCmdDir executes system command in given directory 473 | // and return stdout, stderr in string type, along with possible error. 474 | func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) { 475 | bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...) 476 | return string(bufOut), string(bufErr), err 477 | } 478 | 479 | // ExecCmd executes system command 480 | // and return stdout, stderr in string type, along with possible error. 481 | func ExecCmd(cmdName string, args ...string) (string, string, error) { 482 | return ExecCmdDir("", cmdName, args...) 483 | } 484 | ``` 485 | 486 | - Methods of struct should be put after struct definition, and order them by the order of fields they mostly operate on: 487 | 488 | ```go 489 | type Webhook struct { ... } 490 | func (w *Webhook) GetEvent() { ... } 491 | func (w *Webhook) SaveEvent() error { ... } 492 | func (w *Webhook) HasPushEvent() bool { ... } 493 | ``` 494 | 495 | - If a struct has operational functions, should basically follow the `CRUD` order: 496 | 497 | ```go 498 | func CreateWebhook(w *Webhook) error { ... } 499 | func GetWebhookById(hookId int64) (*Webhook, error) { ... } 500 | func UpdateWebhook(w *Webhook) error { ... } 501 | func DeleteWebhook(hookId int64) error { ... } 502 | ``` 503 | 504 | - If a struct has functions or methods start with `Has`, `Is`, `Can` or `Allow`, they should be ordered by the same order of `Has`, `Is`, `Can` or `Allow`. 505 | 506 | - Declaration of variables should be put before corresponding functions or methods: 507 | 508 | ```go 509 | var CmdDump = cli.Command{ 510 | Name: "dump", 511 | ... 512 | Action: runDump, 513 | Flags: []cli.Flag{}, 514 | } 515 | 516 | func runDump(*cli.Context) { ... 517 | ``` 518 | 519 | ## Testing 520 | 521 | - Unit tests must use [github.com/stretchr/testify](https://github.com/stretchr/testify) and code coverage must above 80%. 522 | 523 | ### Examples 524 | 525 | - The file of examples of helper modules should be named as `example_test.go`. 526 | - Test cases of functions must start with `Test`, e.g. `TestLogger`. 527 | - Test cases of methods must use format `Text_`, e.g. `TestMacaron_Run`. 528 | 529 | ## Linting 530 | 531 | **Warnings will be ignored in practice**, CI should either fail or pass on linting, anything that is not failing the CI should be removed. 532 | 533 | Here is the best practice for configuring `.golangci.yml`: 534 | 535 | ```yml 536 | linters-settings: 537 | nakedret: 538 | max-func-lines: 0 # Disallow any unnamed return statement 539 | 540 | linters: 541 | enable: 542 | - deadcode 543 | - errcheck 544 | - gosimple 545 | - govet 546 | - ineffassign 547 | - staticcheck 548 | - structcheck 549 | - typecheck 550 | - unused 551 | - varcheck 552 | - nakedret 553 | - gofmt 554 | - rowserrcheck 555 | - unconvert 556 | - goimports 557 | ``` 558 | 559 | --------------------------------------------------------------------------------