yangmao 2 жил өмнө
commit
f9499918cf

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 28 - 0
Makefile

@@ -0,0 +1,28 @@
+# 编译生成到bin目录下
+define build
+    sh ./build.sh $(1) ./bin/$(strip $(2))
+endef
+
+clear_dir=./bin
+
+# 脚手架脚本
+go-build: pre
+	$(call build, cmd/go-build/main.go, go-build)
+# 自动生成mode,dao的脚本
+go-orm:
+	$(call build, cmd/go-orm/main.go, go-orm)
+# 自动生成controller,service,third的脚本
+go-new:
+	$(call build, cmd/go-new/main.go, go-new)
+
+.PHONY : clean
+
+# 清理文件
+clean:
+	-rm -rf $(clear_dir)
+
+# 编译静态资源
+pre:
+	$(call build, cmd/go-bindata/main.go, go-bindata) \
+	&& bin/go-bindata -o ./static/go-static.go -pkg=static  static/file
+	

+ 385 - 0
README.md

@@ -0,0 +1,385 @@
+## 组件:
+
+### [go-orm orm代码生产工具](#go-orm)
+
+### [go-new 业务代码生成工具](#go-new)
+
+### [go-build 脚手架生成工具](#go-build)
+
+### [git-hook 代码检测工具](#git-hooksh)
+
+## go-orm
+
+用于生成Go的Model文件,数据库操作太过于麻烦,依靠工具可以直接生成model对象,默认使用的是xorm映射。
+
+> ​	首先声明 : 公司内部的数据库表字段全部是`下划线模式`,表名称全部是`下划线模式`
+
+- 支持生成xorm的model对象
+- 支持生成dao对象
+- 支持生成dto对象(time.Time 转化成 int64时间搓)
+- 支持外部配置文件,防止重复输入配置文件,默认配置文件在 `go-orm-config.json`,这个优先级低于 命令,如果你命令传入-config,显示申明配置文件,那么它的优先级最高。
+
+需要支持Go mod ,所以版本最好V1.13版本以上
+
+### 编译
+
+```go
+export GOPRIVATE="*gitlab*" && go get -u -v gitea.ckfah.com/go-script/cmd/go-orm
+```
+
+### 快速开始
+
+- 配置文件如下:
+
+```json
+{
+  "db_type": "mysql",
+  "tags": [
+    "xorm"
+  ],
+  "db_name": "urban_violation",
+  "db_host": "10.9.198.84",
+  "db_port": 3306,
+  "db_user_name": "ttyc",
+  "db_password": "ttyongche@2014",
+  "db_charset": "utf8"
+}
+```
+
+- 帮助
+
+```shell
+➜  bin go-orm -h
+load config /Users/dong/go/bin/go-orm-config.json
+generator version: anthony/1.0.0
+Usage: generator -host=localhost -port=3306 -d=urban_v -u=root -p=123456 -t=class -t=student -tag=xorm -dir=./tmp
+Option:
+  -charset string
+    	database table names, eg: -charset=utf8 (default "utf8")
+  -config string
+    	orm config file, the shell instrument is priority than config file
+  -d string
+    	database name, eg: -d=xorm
+  -dao_package string
+    	package name default dao, eg:-dao_package=dao (default "dao")
+  -db_type string
+    	database type, eg: -db_type=mysql (default "mysql")
+  -dir string
+    	generated directory default ./tmp, eg: -dir=./tmp (default "./tmp")
+  -dto_package string
+    	package name default dao, eg:-dto_package=dao (default "dto")
+  -h	this help
+  -host string
+    	database host, eg: -port=localhost (default "localhost")
+  -model_package string
+    	package name default model, eg:-model_package=model (default "model")
+  -p string
+    	database password, eg: -p=123456 (default "123456")
+  -port int
+    	database port, eg: -port=3306 (default 3306)
+  -t value
+    	database table names default all tables , eg: -t=class -t=user
+  -tag value
+    	modle tag names support add many tags,default xorm, eg: -tag=xorm -tag=json
+  -u string
+    	database username, eg: -u=root (default "root")
+  -v	generator version
+```
+
+- 开始
+
+下面我使用的是配置文件,你也可以使用参数覆盖配置文件的属性
+
+```go
+➜  bin go-orm -t=op_worker -d=ebike
+load config /Users/dong/go/bin/go-orm-config.json
+[GEN-INFO] save path: /Users/dong/go/bin/tmp
+[xorm] [info]  2020/10/30 19:17:50.721009 PING DATABASE mysql
+[GEN-INFO] save op_worker model success, path=/Users/dong/go/bin/tmp/model/op_worker.go
+[GEN-INFO] save op_worker dao success, path= /Users/dong/go/bin/tmp/dao/op_worker_dao.go
+[GEN-INFO] save ebike dto success, path= /Users/dong/go/bin/tmp/dto/ebike_dto.go
+generate template finished
+```
+
+## go-build
+
+> ​	用于快速构建脚手架项目
+
+### 编译
+
+```go
+export GOPRIVATE="*gitlab*" && go get -u -v gitea.ckfah.com/go-script/cmd/go-build
+```
+
+### 使用
+
+- 帮助
+
+```shell
+➜  go-script git:(master) ✗ ./bin/go-build -h
+[GO-BUILD] 2020/11/30 11:20:36 main.go:173: [INFO] \n================go-build  help=====================
+go-build -dir=/data/temp -mod=go-template -git=git@gitlab.corp.cjjyProject:ebike-urban-op/go-template.git -branch=gauss -vendor=true
+Option:
+构建master分支(例如:项目本地地址/data/ebike-op-demo,项目名称为:ebike-op-demo):
+        go-build -dir=/data/ebike-op-demo -mod=ebike-op-demo
+构建gauss分支:
+    go-build -dir=/data/temp -mod=go-template -branch=gauss
+构建vendor项目:
+        go-build -dir=/data/temp -mod=go-template -vendor=true
+
+  -branch string
+        git branch, eg:-branch=master (default "master")
+  -dir string
+        git clone dir if not set default go mod project name, eg:-dir=/data/temp
+  -git string
+        git remote addr, eg:-git=git@gitlab.corp.cjjyProject:ebike-urban-op/go-template.git (default "git@gitlab.corp.cjjyProject:ebike-urban-op/go-template.git")
+  -h    this help
+  -mod string
+        go mod name, eg:-mod=go-template (default "go-template")
+  -vendor
+        use go vendor mod, default disable
+```
+
+- 使用
+
+```go
+go-build -dir=./go-template -mod=go-template -git=git@gitlab.corp.cjjyProject:ebike-urban-op/go-template.git  -branch=master
+```
+
+注意可能使用其他分支构建,默认是 master分支,比如你使用`gauss`分支:
+
+```shell
+go-build -dir=./go-template -mod=go-template -git=git@gitlab.corp.cjjyProject:ebike-urban-op/go-template.git  -branch=gauss
+```
+
+## git-hook.sh
+
+> 帮助GitHook 自动检测代码,规范代码,格式化代码。
+
+主要利用工具 `go fmt` , `go import`
+
+- 使用:
+
+```shell
+./git-hook.sh
+```
+
+- 文件位置:
+
+[执行脚本](./shell/new-githook.shell)
+
+[代码检测脚本](./shell/code.shell)
+
+[代码Commit-Comment脚本](./shell/message.shell)
+
+## go-new
+
+> ​	用来生产我们使用脚手架的一些通用的代码
+
+### 编译
+
+```shell
+export GOPRIVATE="*gitlab*" && go get -u -v gitea.ckfah.com/go-script/cmd/go-new
+```
+
+### 使用
+
+- 使用帮助
+
+```shell
+➜  bin go-new -h
+================go-new  help=====================
+go-new -r=/user/list -s=user
+Option:
+  -c string
+    	方法注释!!service方法和controller方法注释是一样的奥!!不传不生成
+  -h	帮助!!
+  -oc
+    	只生成Controller方法
+  -od
+    	比如 -od 只生成Dto对象
+  -on
+    	只生成New方法
+  -os
+    	只生成Service方法
+  -r string
+    	请求路由,比如/echo,生成Echo方法,/user/list生成UserList,必须传递
+  -s string
+    	生成的Service/Controller名字,比如 user,则生成userService,必须传递
+  -t	生成third,注意也是复用router 和 service_name,所以必须传递-r -s!!
+  -ug
+    	使用gin框架,默认是gauss
+```
+
+- 生成service ,controller
+
+```shell
+➜  bin go-new -s=user -r=user/info -c="查看用户信息"
+==================touch file=======================
+controller: user_controller.go
+service: user_service.go
+dto: user_dto.go
+
+
+// =======================user_controller.go==============================
+
+type userController struct {
+}
+
+func NewUserController() *userController {
+	return new(userController)
+}
+
+var (
+	userService = service.NewUserService()
+)
+
+// =======================user_service.go==============================
+
+type userService struct {
+}
+
+func NewUserService() *userService {
+	return new(userService)
+}
+
+
+// =======================user_controller.go==============================
+
+// 查看用户信息
+func (this *userController) UserInfo(ctx *engines.Context) {
+	request := new(dto.UserInfoRequest)
+	err := ctx.BindJSON(request)
+	if err != nil {
+		logger.Warnc(ctx, "[UserInfo] bind params err,err=%v", err)
+		util.FailJson(ctx, exception.ParamsBindError)
+		return
+	}
+	logger.Infoc(ctx, "[UserInfo] start,request=%+v", request)
+	result, cerr := userService.UserInfo(ctx, &request.Params)
+	if cerr != nil {
+		logger.Warnc(ctx, "[UserInfo] end err,err=%v", cerr)
+		util.FailJson(ctx, cerr)
+		return
+	}
+	logger.Infoc(ctx, "[UserInfo] end,result=%+v", result)
+	util.SuccessJson(ctx, result)
+}
+
+// =======================user_dto.go==============================
+// 查看用户信息
+type UserInfoRequest struct {
+	Params UserInfoParams `json:"params" binding:"required"`
+}
+
+type UserInfoParams struct {
+}
+
+type UserInfoResult struct {
+}
+
+
+// =======================user_service.go==============================
+
+// 查看用户信息
+func (this *userService) UserInfo(context *engines.Context, params *dto.UserInfoParams) (*dto.UserInfoResult, cerror.Cerror) {
+	return nil, nil
+}
+
+
+// ======================service.go==================================
+UserInfoError = util.NewSCerror(userInfoErrorCode)
+```
+
+
+
+- 生成third
+
+```shell
+➜  bin go-new -s=user -r=user/info -c="查看用户信息" -t
+==================touch file=======================
+third: user_third.go
+dto: user_dto.go
+注意配置 apollo or env
+
+// ===========================user_third.go===========================
+
+// api-doc-address:
+type userThird struct {
+}
+
+func NewUserThird() *userThird {
+	return &userThird{}
+}
+
+const (
+	_UserThirdServerName = "user"
+)
+
+func (*userThird) GetServerName() string {
+	return _UserThirdServerName
+}
+
+// 查看用户信息
+func (this *userThird) UserInfo(ctx *engines.Context, params *dto.UserInfoParams) (*dto.UserInfoResponse, cerror.Cerror) {
+	path := "/user/info"
+	response := new(dto.UserInfoResponse)
+	err := util.HttpRequestAndDecode(ctx, this.GetServerName(), path, params, response)
+	if err != nil {
+		logger.Warnc(ctx, "[UserInfo] request err,params=%+v,err=%v", params, err)
+		return nil, exception.UserInfoError(err)
+	}
+	return response, nil
+}
+
+
+// =======================user_dto.go==============================
+// 查看用户信息
+type UserInfoParams struct {
+}
+
+type UserInfoResponse struct {
+}
+
+
+//======================= config/apollo/namespaces.json =======================
+
+{
+    "name": "user",
+    "keys": [
+    {
+        "name": "user.host",
+        "mapTo": "host"
+    },
+    {
+        "name": "user.key",
+        "mapTo": "key"
+    },
+    {
+        "name": "user.name",
+        "mapTo": "name"
+    }
+    ]
+}
+
+//======================= apollo添加properties,注意host key=======================
+
+# user 服务
+user.host=
+user.key=
+user.name=user
+
+
+//=======================如果不是apollo,请在env.ini中添加,注意host key=======================
+
+;user 服务
+[user]
+host=
+key=
+name=user
+
+
+// ======================third.go==================================
+UserInfoError = util.NewECerror(userInfoErrorCode)
+```
+

+ 46 - 0
build.sh

@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+
+set -e
+
+help_msg(){
+    echo "Usage: $0 <main_path> <binary_name>"
+}
+
+if [[ "$1" == "-h" || "$1" == "--help" ]] ;then
+    help_msg
+    exit 1
+fi
+
+if [[ $# != 2 ]];then
+    echo "Parameter incorrect."
+    help_msg
+    exit 1
+fi
+
+
+# 主函数的位置
+RUN_PATH="$1"
+
+# 输出二进制文件名称
+OUT_PATH="$2"
+GO=`which go`
+OLDGO111MODULE="$GO111MODULE"
+OLDGOPROXY="$GOPROXY"
+OLDGOPRIVATE="$GOPRIVATE"
+
+export GO111MODULE=on
+export GOPROXY=https://goproxy.cn
+export GOPRIVATE="*gitea*"
+
+echo "${GO} build -o ${OUT_PATH} -race -work -v -ldflags "-s" -gcflags "-N -l" ${RUN_PATH}"
+${GO} build -o ${OUT_PATH} -race -work -v -ldflags "-s" -gcflags "-N -l" ${RUN_PATH}
+
+# reset env
+export GO111MODULE="$OLDGO111MODULE"
+export GOPROXY="$OLDGOPROXY"
+export GOPRIVATE="$OLDGOPRIVATE"
+
+# 添加可执行权限
+chmod +x "${OUT_PATH}"
+
+echo "build ${RUN_PATH} success, out binary file path: ${OUT_PATH}"

+ 59 - 0
cmd/filter/main.go

@@ -0,0 +1,59 @@
+package main
+
+import (
+	"encoding/json"
+	"flag"
+	"fmt"
+	"os"
+
+	"gitea.ckfah.com/go-script/file"
+	"gitea.ckfah.com/go-script/logger"
+)
+
+var (
+	rp string
+)
+
+type FilterS struct {
+	Filter []string `json:"filter"`
+}
+
+func main() {
+	flag.StringVar(&rp, "p", "", "")
+	flag.Parse()
+	if rp == "" {
+		logger.FatalF("rp path is nil")
+	}
+	path, err := file.GetFilePath(".")
+	if err != nil {
+		logger.FatalF("rp path not found, err=%s", err)
+	}
+
+	filePath, err := file.GetFilePath("filter-config.json")
+	if err != nil {
+		logger.FatalF("filter-config.json not found, err=%s", err)
+	}
+	configF, err := os.Open(filePath)
+	if err != nil {
+		logger.FatalF("open file err,err=%s", err)
+	}
+	defer configF.Close()
+	filter := new(FilterS)
+	err = json.NewDecoder(configF).Decode(filter)
+	if err != nil {
+		logger.FatalF("json config decode err,err=%s", err)
+	}
+	if filter.Filter == nil || len(filter.Filter) == 0 {
+		success()
+	}
+	err = file.ReplacePackageV2(path, filter.Filter, []string{"vendor", ".git", "bin", ".DS_Store", "LICENSE"})
+	if err != nil {
+		logger.FatalF("rp  err,err=%s", err)
+	}
+	success()
+}
+
+func success() {
+	fmt.Println("replace all success")
+	os.Exit(-1)
+}

+ 107 - 0
cmd/go-bindata/main.go

@@ -0,0 +1,107 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	bindata "gitea.ckfah.com/go-script/internal"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+)
+
+func main() {
+	cfg := parseArgs()
+	err := bindata.Translate(cfg)
+
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "bindata: %v\n", err)
+		os.Exit(1)
+	}
+}
+
+// parseArgs create s a new, filled configuration instance
+// by reading and parsing command line options.
+//
+// This function exits the program with an error, if
+// any of the command line options are incorrect.
+func parseArgs() *bindata.Config {
+	var version bool
+
+	c := bindata.NewConfig()
+
+	flag.Usage = func() {
+		fmt.Printf("Usage: %s [options] <input directories>\n\n", os.Args[0])
+		flag.PrintDefaults()
+	}
+
+	flag.BoolVar(&c.Debug, "debug", c.Debug, "Do not embed the assets, but provide the embedding API. Contents will still be loaded from disk.")
+	flag.BoolVar(&c.Dev, "dev", c.Dev, "Similar to debug, but does not emit absolute paths. Expects a rootDir variable to already exist in the generated code's package.")
+	flag.StringVar(&c.Tags, "tags", c.Tags, "Optional set of build tags to include.")
+	flag.StringVar(&c.Prefix, "prefix", c.Prefix, "Optional path prefix to strip off asset names.")
+	flag.StringVar(&c.Package, "pkg", c.Package, "Package name to use in the generated code.")
+	flag.BoolVar(&c.NoMemCopy, "nomemcopy", c.NoMemCopy, "Use a .rodata hack to get rid of unnecessary memcopies. Refer to the documentation to see what implications this carries.")
+	flag.BoolVar(&c.NoCompress, "nocompress", c.NoCompress, "Assets will *not* be GZIP compressed when this flag is specified.")
+	flag.BoolVar(&c.NoMetadata, "nometadata", c.NoMetadata, "Assets will not preserve size, mode, and modtime info.")
+	flag.BoolVar(&c.HttpFileSystem, "fs", c.HttpFileSystem, "Whether generate instance http.FileSystem interface code.")
+	flag.UintVar(&c.Mode, "mode", c.Mode, "Optional file mode override for all files.")
+	flag.Int64Var(&c.ModTime, "modtime", c.ModTime, "Optional modification unix timestamp override for all files.")
+	flag.StringVar(&c.Output, "o", c.Output, "Optional name of the output file to be generated.")
+	flag.BoolVar(&version, "version", false, "Displays version information.")
+
+	ignore := make([]string, 0)
+	flag.Var((*bindata.AppendSliceValue)(&ignore), "ignore", "Regex pattern to ignore")
+
+	flag.Parse()
+
+	patterns := make([]*regexp.Regexp, 0)
+	for _, pattern := range ignore {
+		patterns = append(patterns, regexp.MustCompile(pattern))
+	}
+	c.Ignore = patterns
+
+	if version {
+		fmt.Printf("%s\n", bindata.Version())
+		os.Exit(0)
+	}
+
+	// Make sure we have input paths.
+	if flag.NArg() == 0 {
+		fmt.Fprintf(os.Stderr, "Missing <input dir>\n\n")
+		flag.Usage()
+		os.Exit(1)
+	}
+
+	// Create input configurations.
+	c.Input = make([]bindata.InputConfig, flag.NArg())
+	for i := range c.Input {
+		c.Input[i] = parseInput(flag.Arg(i))
+	}
+
+	return c
+}
+
+// parseRecursive determines whether the given path has a recursive indicator and
+// returns a new path with the recursive indicator chopped off if it does.
+//
+//  ex:
+//      /path/to/foo/...    -> (/path/to/foo, true)
+//      /path/to/bar        -> (/path/to/bar, false)
+func parseInput(path string) bindata.InputConfig {
+	if strings.HasSuffix(path, "/...") {
+		return bindata.InputConfig{
+			Path:      filepath.Clean(path[:len(path)-4]),
+			Recursive: true,
+		}
+	} else {
+		return bindata.InputConfig{
+			Path:      filepath.Clean(path),
+			Recursive: false,
+		}
+	}
+
+}

+ 185 - 0
cmd/go-build/main.go

@@ -0,0 +1,185 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"gitea.ckfah.com/go-script/file"
+	"gitea.ckfah.com/go-script/logger"
+	"gitea.ckfah.com/go-script/scrpit"
+	"gitea.ckfah.com/go-script/static"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+var (
+	gitRemoteAddress string // 克隆的地址
+	branch           string // 克隆的分支
+	cloneDir         string // 克隆的地址
+	modelName        string // go-mod 项目名称
+	helps            bool   // 帮助
+	vendor           bool   // 是否使用vendor模式
+
+	log = logger.NewStdOutLogger("[GO-BUILD]")
+)
+
+const (
+	propertiesFile = "application.properties"
+	templateName   = "go-template"
+)
+
+func init() {
+	flag.StringVar(&modelName, "mod", "go-template", "go mod name, eg:-mod=go-template")
+	flag.StringVar(&gitRemoteAddress, "git", "git@gitlab.corp.cjjyProject:ebike-urban-op/go-template.git", "git remote addr, eg:-git=git@gitlab.corp.cjjyProject:ebike-urban-op/go-template.git")
+	flag.StringVar(&branch, "branch", "master", "git branch, eg:-branch=master")
+	flag.StringVar(&cloneDir, "dir", "", "git clone dir if not set default go mod project name, eg:-dir=/data/temp")
+	flag.BoolVar(&vendor, "vendor", false, "use go vendor mod, default disable")
+	flag.BoolVar(&helps, "h", false, "this help")
+}
+
+func main() {
+	// 初始化脚本命令
+	initFlag()
+	if cloneDir == "" {
+		cloneDir = "./" + modelName
+	}
+	dir, err := filepath.Abs(cloneDir)
+	if err != nil {
+		log.Fatalf("get clone path err, err: %v", err)
+	}
+	log.Infof("clone git file into path: %s", dir)
+
+	defer func() {
+		if err := recover(); err != nil {
+			if err == logger.ExitError {
+				scrpit.Delete(dir)
+			}
+		}
+	}()
+
+	// 克隆git
+	cloneGit(dir)
+
+	// 重命名项目的一些内容
+	err = file.NewTemplate(dir, templateName, modelName)
+	if err != nil {
+		log.Fatalf("build template err, err: %v", err)
+	}
+	log.Infof("build template success")
+
+	// 删除模版无效文件
+	deleteTemplateFile(dir)
+
+	// copy 配置文件
+	initTemplateConfig(dir)
+
+	// 处理vendor模式
+	handlerOpenVendor(dir)
+
+	// 处理readme
+	scrpit.Mv(join(dir, "README_project.md"), join(dir, "README.md"))
+	log.Infof("init project README.md success!")
+
+	// 处理预设定配置文件
+	err = writeProperties(dir)
+	if err != nil {
+		log.Fatalf("copy properties find err, err: %v", err)
+	}
+
+	log.Infof(`初始化 git hook 中..... 如果失败请进入项目手动执行!!!  git init && curl -O https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/new-githook.shell && bash new-githook.shell && rm new-githook.shell`)
+	str := `cd %s && git init && curl -O https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/new-githook.shell && bash new-githook.shell && rm new-githook.shell`
+	shell := fmt.Sprintf(str, dir)
+	scrpit.Run(shell)
+
+	log.Infof(`你现在可以进入目录执行, make , 然后请求  curl -H "Content-type: application/json" -X POST -d '{"params":{"id":1}}' http://localhost:13058/report/info`)
+	log.Infof(`初始化项目成功,谢谢使用!!!!`)
+}
+
+func initTemplateConfig(dir string) {
+	scrpit.Copy(join(dir, "config/env.dev.ini"), join(dir, "config/env.ini"))
+	scrpit.Copy(join(dir, "config/apollo/apollo.dev.json"), join(dir, "config/apollo/apollo.json"))
+	log.Infof("copy config success !")
+}
+
+func deleteTemplateFile(dir string) {
+	scrpit.Delete(join(dir, ".git"))
+	log.Infof("delete .git file success")
+}
+
+func cloneGit(dir string) {
+	log.Infof("git clone %s to %s\n", gitRemoteAddress, dir)
+	if branch == "master" {
+		scrpit.Git(gitRemoteAddress, dir)
+	} else {
+		scrpit.GitBranch(branch, gitRemoteAddress, dir)
+	}
+	log.Infof("git clone -b %s %s %s success", branch, gitRemoteAddress, dir)
+}
+
+func handlerOpenVendor(dir string) {
+	if vendor {
+		scrpit.Copy(join(dir, ".gitignore_vendor"), join(dir, ".gitignore"))
+		scrpit.Copy(join(dir, "Makefile_vendor"), join(dir, "Makefile"))
+		log.Infof("copy make file and .gitignore success because enable go mod vendor")
+	}
+	scrpit.Delete(join(dir, "Makefile_vendor"))
+	scrpit.Delete(join(dir, ".gitignore_vendor"))
+	log.Infof("handler vendor handler success!")
+}
+
+func writeProperties(dir string) error {
+	properties, err := static.Asset("static/file/application.properties")
+	if err != nil {
+		return err
+	}
+	newProperties := strings.ReplaceAll(string(properties), templateName, modelName)
+	propertiesFilePath := join(dir, propertiesFile)
+	propertiesFile, err := os.OpenFile(propertiesFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.FileMode(0644))
+	if err != nil {
+		panic(err)
+	}
+	defer propertiesFile.Close()
+	_, err = propertiesFile.Write([]byte(newProperties))
+	if err != nil {
+		return err
+	}
+	log.Infof("copy application.properties success,使用 nacos/apollo 需要使用这个配置文件 %s, 复制到配置中心去!", propertiesFilePath)
+	return nil
+}
+
+func echoLine() {
+	fmt.Println()
+}
+
+func join(dir, file string) string {
+	return filepath.Clean(fmt.Sprintf("%s%s%s", dir, string(os.PathSeparator), file))
+}
+
+func initFlag() {
+	flag.Parse()
+	if helps {
+		printHelp()
+		os.Exit(-1)
+	}
+	if modelName == "" {
+		log.Infof("please set -mod=your_project_name")
+		printHelp()
+		os.Exit(-1)
+	}
+	log.Infof("Init flag success")
+}
+
+func printHelp() {
+	log.Infof(`\n================go-build  help=====================
+go-build -dir=/data/temp -mod=go-template -git=git@gitlab.corp.cjjyProject:ebike-urban-op/go-template.git -branch=gauss -vendor=true
+Option:
+构建master分支(例如:项目本地地址/data/ebike-op-demo,项目名称为:ebike-op-demo):
+	go-build -dir=/data/ebike-op-demo -mod=ebike-op-demo
+构建gauss分支:
+    go-build -dir=/data/temp -mod=go-template -branch=gauss
+构建vendor项目:
+	go-build -dir=/data/temp -mod=go-template -vendor=true
+
+`)
+	flag.PrintDefaults()
+}

+ 488 - 0
cmd/go-new/main.go

@@ -0,0 +1,488 @@
+package main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"go/format"
+	"os"
+	"strings"
+	"text/template"
+
+	"gitea.ckfah.com/go-script/logger"
+	"gitea.ckfah.com/go-script/utils"
+)
+
+type ExType string
+
+const (
+	ServiceType = ExType("service.go")
+	ThirdType   = ExType("third.go")
+)
+
+type Controller struct {
+	RouterName  string
+	URouterName string
+	Service     string
+	UserGin     bool
+	Comment     string
+	ExType      ExType
+}
+
+//
+func (c Controller) ExceptionType() string {
+	return string(c.ExType)
+}
+
+//
+
+func (c Controller) GetServiceFile() string {
+	return utils.UnMarshal(fmt.Sprintf("%sService.go", c.Service))
+}
+func (c Controller) GetControllerFile() string {
+	return utils.UnMarshal(fmt.Sprintf("%sController.go", c.Service))
+}
+func (c Controller) GetDtoFile() string {
+	return utils.UnMarshal(fmt.Sprintf("%sDto.go", c.Service))
+}
+
+// third
+func (c Controller) GetThirdName() string {
+	return utils.UnMarshal(fmt.Sprintf("%sThird.go", c.Service))
+}
+
+func (c Controller) ThirdName() string {
+	return fmt.Sprintf("%sThird", c.Service)
+}
+
+func (c Controller) ThirdServiceApolloName() string {
+	return strings.ReplaceAll(utils.UnMarshal(c.Service), "_", "-")
+}
+
+// 处理 !
+func NewController(router string, serviceName string) Controller {
+	// 空
+	if router == "" || serviceName == "" {
+		logger.FatalFH("router_name 和 service_name 不能为空!!!", printHelp)
+	}
+	// 非法字符 !
+	if serviceName[0] >= '0' && serviceName[0] <= '9' {
+		logger.FatalFH("-s=? 不能首字母是数字", printHelp)
+	}
+	if router[0] >= '0' && router[0] <= '9' {
+		logger.FatalFH("-t=? 不能首字母是数字", printHelp)
+	}
+
+	// 保存最原始的r
+	oldRouter := router
+	router = strings.TrimPrefix(router, "/")
+	router = strings.TrimSuffix(router, "/")
+	// add /
+	oldRouter = "/" + router
+
+	// r 去除service_name
+	//if strings.HasPrefix(router, serviceName) {
+	//	router = strings.TrimPrefix(router, serviceName)
+	//}
+
+	// service-name  转换为 驼峰
+	serviceName = utils.Marshal(strings.ReplaceAll(serviceName, "-", "_"))
+	if strings.HasSuffix(serviceName, "Service") {
+		serviceName = strings.TrimSuffix(serviceName, "Service")
+	}
+	if strings.HasSuffix(serviceName, "Controller") {
+		serviceName = strings.TrimSuffix(serviceName, "Controller")
+	}
+	if strings.HasSuffix(serviceName, "Third") {
+		serviceName = strings.TrimSuffix(serviceName, "Third")
+	}
+
+	// 转换为下划线 !!!
+	if strings.Contains(router, "/") {
+		split := strings.Split(router, "/")
+		router = strings.Join(split, "_")
+	}
+	router = strings.ReplaceAll(router, "-", "_")
+	// 初始化!!!!
+	return Controller{
+		RouterName:  utils.Marshal(router),
+		URouterName: oldRouter,
+		Service:     serviceName,
+	}
+}
+
+func (c Controller) ContextName() string {
+	if userGin {
+		return "*gin.Context"
+	}
+	return "*engines.Context"
+}
+
+func (c Controller) Common() string {
+	if c.Comment == "" {
+		return ""
+	}
+	return fmt.Sprintf("// %s", c.Comment)
+}
+
+func (c Controller) GetPath() string {
+	return c.URouterName
+}
+
+func (c Controller) ControllerName() string {
+	if c.Service == "" {
+		logger.FatalF("-s 不能为空!!!!,否则无法生成模版")
+	}
+	return fmt.Sprintf("%s%s", LowerName(c.Service), "Controller")
+}
+
+func (c Controller) FuncName() string {
+	return UpperName(c.RouterName)
+}
+
+func (c Controller) RequestName() string {
+	return fmt.Sprintf("%s%s", UpperName(c.RouterName), "Request")
+}
+
+func (c Controller) ServiceName() string {
+	if c.Service == "" {
+		logger.FatalF("-s 不能为空!!!!,否则无法生成模版")
+	}
+	return fmt.Sprintf("%s%s", LowerName(c.Service), "Service")
+}
+
+func (c Controller) Params() string {
+	return "params"
+}
+
+func LowerName(str string) string {
+	if str[0] >= 'A' && str[0] <= 'Z' {
+		newStr := []byte(str)
+		newStr[0] = newStr[0] + 32
+		return string(newStr)
+	}
+	return str
+}
+
+func UpperName(str string) string {
+	if str[0] >= 'a' && str[0] <= 'z' {
+		newStr := []byte(str)
+		newStr[0] = newStr[0] - 32
+		return string(newStr)
+	}
+	return str
+}
+
+func NewJsonTag(str string) string {
+	return fmt.Sprintf("`json:\"%s\" binding:\"required\"`", str)
+}
+
+var newT = `
+// ======================={{.GetControllerFile}}==============================
+
+type {{LowerName .ControllerName}} struct {
+}
+
+func New{{UpperName .ControllerName}}() *{{LowerName .ControllerName}} {
+	return new({{LowerName .ControllerName}})
+}
+
+var (
+{{LowerName .ServiceName}} = service.New{{UpperName .ServiceName}}()
+)
+
+// ======================={{.GetServiceFile}}==============================
+
+type {{LowerName .ServiceName}} struct {
+}
+
+func New{{UpperName .ServiceName}}() *{{LowerName .ServiceName}} {
+	return new({{LowerName .ServiceName}})
+}
+`
+
+var (
+	controllerT = `
+// ======================={{.GetControllerFile}}==============================
+
+{{.Common}}
+func(this *{{.ControllerName}}){{.FuncName}}(ctx {{.ContextName}}){
+    	request := new(vo.{{.RequestName}})
+    	err := ctx.BindJSON(request)
+    	if err != nil {
+    		logger.Warnc(ctx, "[{{.FuncName}}] bind params err,err=%v", err)
+    		common.FailJson(ctx, exception.ParamsBindError)
+    		return
+    	}
+    	logger.Infoc(ctx, "[{{.FuncName}}] start,request=%+v", request)
+    	result, cerr := {{.ServiceName}}.{{.FuncName}}(ctx, &request.Params)
+    	if cerr != nil {
+    		logger.Warnc(ctx, "[{{.FuncName}}] end err,err=%v", cerr)
+    		common.FailJson(ctx, cerr)
+    		return
+    	}
+        logger.Infoc(ctx, "[{{.FuncName}}] end,result=%+v", result)
+    	common.SuccessJson(ctx, result)
+}`
+)
+
+var (
+	dtoT = `
+// ======================={{.GetDtoFile}}==============================
+{{.Common}}
+type {{UpperName .FuncName}}Request struct {
+	Params dto.{{UpperName .FuncName}}Params {{NewJsonTag .Params}}
+}
+
+type {{UpperName .FuncName}}Params struct {
+}
+
+
+type {{UpperName .FuncName}}Result struct {
+	
+}
+`
+)
+
+var (
+	serviceT = `
+// ======================={{.GetServiceFile}}==============================
+
+{{.Common}}
+func (this *{{LowerName .ServiceName}}) {{UpperName .FuncName}}(context {{.ContextName}}, params *dto.{{UpperName .FuncName}}Params) (*dto.{{UpperName .FuncName}}Result, cerror.Cerror) {
+	return nil, nil
+}
+`
+)
+
+var (
+	thirdT = `
+// ==========================={{.GetThirdName}}===========================
+
+// api-doc-address: 
+type {{LowerName .ThirdName}} struct {
+}
+
+func New{{UpperName .ThirdName}}() *{{LowerName .ThirdName}} {
+	return &{{LowerName .ThirdName}}{}
+}
+
+const (
+	_{{UpperName .ThirdName}}ServerName = "{{.ThirdServiceApolloName}}"
+)
+
+func (*{{LowerName .ThirdName}}) GetServerName() string {
+	return _{{UpperName .ThirdName}}ServerName
+}
+
+{{.Common}}
+func (this *{{LowerName .ThirdName}}) {{UpperName .FuncName}}(ctx {{.ContextName}},params *dto.{{UpperName .FuncName}}Params) (*dto.{{UpperName .FuncName}}Response, cerror.Cerror) {
+	path := "{{.GetPath}}"
+	response := new(dto.{{UpperName .FuncName}}Response)
+	err := util.HttpRequestAndDecode(ctx, this.GetServerName(), path, params, response)
+	if err != nil {
+		logger.Warnc(ctx, "[{{UpperName .FuncName}}] request err,params=%+v,err=%v", params, err)
+		return nil, exception.{{UpperName .FuncName}}Error(err)
+	}
+	return response, nil
+}
+`
+)
+
+var (
+	thirdDtoT = `
+// ======================={{.GetDtoFile}}==============================
+{{.Common}}
+type {{UpperName .FuncName}}Params struct {
+}
+
+type {{UpperName .FuncName}}Response struct {
+	
+}
+`
+)
+
+var (
+	apolloConfigT = `
+//======================= config/apollo/namespaces.json =======================
+
+{
+    "name": "{{.ThirdServiceApolloName}}",
+    "keys": [
+    {
+        "name": "{{.ThirdServiceApolloName}}.host",
+        "mapTo": "host"
+    },
+    {
+        "name": "{{.ThirdServiceApolloName}}.key",
+        "mapTo": "key"
+    },
+    {
+        "name": "{{.ThirdServiceApolloName}}.name",
+        "mapTo": "name"
+    }
+    ]
+}
+
+//======================= apollo添加properties,注意host key=======================
+
+# {{.ThirdServiceApolloName}} 服务
+{{.ThirdServiceApolloName}}.host=
+{{.ThirdServiceApolloName}}.key=
+{{.ThirdServiceApolloName}}.name={{.ThirdServiceApolloName}}
+
+
+//=======================如果不是apollo,请在env.ini中添加,注意host key=======================
+
+;{{.ThirdServiceApolloName}} 服务
+[{{.ThirdServiceApolloName}}]
+host=
+key=
+name={{.ThirdServiceApolloName}}
+`
+)
+
+var (
+	exceptionServiceT = `
+// ======================{{.ExceptionType}}==================================
+{{UpperName .FuncName}}Error = util.NewSCerror({{LowerName .FuncName}}ErrorCode)
+`
+)
+
+var (
+	exceptionThirdT = `
+// ======================{{.ExceptionType}}==================================
+{{UpperName .FuncName}}Error = util.NewECerror({{LowerName .FuncName}}ErrorCode)
+`
+)
+
+var (
+	common         string
+	url            string
+	service        string
+	gThird         bool // 生成third
+	helps          bool
+	onlyDto        bool
+	onlyMew        bool
+	onlyService    bool
+	onlyController bool
+	userGin        bool
+)
+
+func main() {
+	flag.StringVar(&url, "r", "", "请求路由,比如/echo,生成Echo方法,/user/list生成UserList,必须传递")
+	flag.StringVar(&service, "s", "", "生成的Service/Controller名字,比如 user,则生成userService,必须传递")
+	flag.BoolVar(&helps, "h", false, "帮助!!")
+	flag.BoolVar(&gThird, "t", false, "生成third,注意也是复用router 和 service_name,所以必须传递-r -s!!")
+	flag.BoolVar(&onlyDto, "od", false, "比如 -od 只生成Dto对象")
+	flag.BoolVar(&onlyMew, "on", false, "只生成New方法")
+	flag.BoolVar(&onlyService, "os", false, "只生成Service方法")
+	flag.BoolVar(&onlyController, "oc", false, "只生成Controller方法")
+	flag.BoolVar(&userGin, "ug", false, "使用gin框架,默认是gauss")
+	flag.StringVar(&common, "c", "", "方法注释!!service方法和controller方法注释是一样的奥!!不传不生成")
+	Init()
+	t := template.New("")
+	t = t.Funcs(template.FuncMap{
+		"LowerName":     LowerName,
+		"UpperName":     UpperName,
+		"NewJsonTag":    NewJsonTag,
+		"UtilUnMarshal": utils.UnMarshal,
+	})
+	controller := NewController(url, service)
+	controller.UserGin = userGin
+	controller.Comment = common
+	fmt.Printf("==================touch file=======================\n")
+	if gThird {
+		controller.ExType = ThirdType
+		fmt.Printf("third: %s\ndto: %s\n注意配置 apollo or env\n",
+			controller.GetThirdName(),
+			controller.GetDtoFile(),
+		)
+		exec(t, thirdT, controller)
+		exec(t, thirdDtoT, controller)
+		execNotFormat(t, apolloConfigT, controller)
+		execNotFormat(t, exceptionThirdT, controller)
+		os.Exit(-1)
+	}
+
+	controller.ExType = ServiceType
+
+	fmt.Printf("controller: %s\nservice: %s\ndto: %s\n\n",
+		controller.GetControllerFile(),
+		controller.GetServiceFile(),
+		controller.GetDtoFile(),
+	)
+
+	if onlyMew {
+		exec(t, newT, controller)
+		os.Exit(-1)
+	}
+	if onlyController {
+		exec(t, controllerT, controller)
+		os.Exit(-1)
+	}
+
+	if onlyDto {
+		exec(t, dtoT, controller)
+		os.Exit(-1)
+	}
+
+	if onlyService {
+		exec(t, serviceT, controller)
+		execNotFormat(t, exceptionServiceT, controller)
+		os.Exit(-1)
+	}
+
+	exec(t, newT, controller)
+	exec(t, controllerT, controller)
+	exec(t, dtoT, controller)
+	exec(t, serviceT, controller)
+	execNotFormat(t, exceptionServiceT, controller)
+	os.Exit(-1)
+
+}
+
+func exec(t *template.Template, str string, data interface{}) {
+	parse, err := t.Parse(str)
+	if err != nil {
+		panic(err)
+	}
+	buffer := &bytes.Buffer{}
+	err = parse.Execute(buffer, data)
+	if err != nil {
+		panic(err)
+	}
+	source, err := format.Source(buffer.Bytes())
+	if err != nil {
+		panic(err)
+	}
+	fmt.Printf("%s\n", source)
+}
+
+func execNotFormat(t *template.Template, str string, data interface{}) {
+	parse, err := t.Parse(str)
+	if err != nil {
+		panic(err)
+	}
+	buffer := &bytes.Buffer{}
+	err = parse.Execute(buffer, data)
+	if err != nil {
+		panic(err)
+	}
+	fmt.Printf("%s\n", buffer.Bytes())
+}
+
+func Init() {
+	flag.Parse()
+	if helps {
+		printHelp()
+		os.Exit(-1)
+	}
+}
+
+func printHelp() {
+	fmt.Println(`================go-new  help=====================
+go-new -r=/user/list -s=user
+Option:`)
+	flag.PrintDefaults()
+}

+ 200 - 0
cmd/go-orm/main.go

@@ -0,0 +1,200 @@
+package main
+
+import (
+	"encoding/json"
+	"flag"
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+
+	"gitea.ckfah.com/go-script/logger"
+	"gitea.ckfah.com/go-script/orm"
+)
+
+const (
+	generatorVersion = "1.0.0"
+	successExit      = -1
+)
+
+var (
+	config *DBConfig = new(DBConfig)
+)
+
+var (
+	configFile       string
+	help             bool
+	showVersion      bool
+	destDir          string
+	modelPackageName string
+	daoPackageName   string
+	dtoPackageName   string
+	tableNames       strFlags
+	dbType           string
+	tags             strFlags
+	dbName           string
+	dbHost           string
+	dbPort           int
+	dbUserName       string
+	dbPassword       string
+	dbCharset        string
+)
+
+type strFlags []string
+
+func (i *strFlags) String() string {
+	return "table names"
+}
+
+func (i *strFlags) Set(value string) error {
+	*i = append(*i, value)
+	return nil
+}
+
+func init() {
+	flag.BoolVar(&help, "h", false, "this help")
+	flag.BoolVar(&showVersion, "v", false, "generator version")
+	flag.StringVar(&dbType, "db_type", "mysql", "database type, eg: -db_type=mysql")
+	flag.StringVar(&dbName, "d", "", "database name, eg: -d=xorm")
+	flag.StringVar(&dbHost, "host", "localhost", "database host, eg: -port=localhost")
+	flag.IntVar(&dbPort, "port", 3306, "database port, eg: -port=3306")
+	flag.StringVar(&dbUserName, "u", "root", "database username, eg: -u=root")
+	flag.StringVar(&dbPassword, "p", "123456", "database password, eg: -p=123456")
+	flag.Var(&tableNames, "t", "database table names default all tables , eg: -t=class -t=user")
+	flag.StringVar(&dbCharset, "charset", "utf8", "database table names, eg: -charset=utf8")
+	flag.Var(&tags, "tag", "modle tag names support add many tags,default xorm, eg: -tag=xorm -tag=json")
+	flag.StringVar(&configFile, "config", "", "orm config file, the shell instrument is priority than config file")
+	flag.StringVar(&destDir, "dir", "./tmp", "generated directory default ./tmp, eg: -dir=./tmp")
+	flag.StringVar(&modelPackageName, "model_package", "model", "package name default model, eg:-model_package=model")
+	flag.StringVar(&daoPackageName, "dao_package", "dao", "package name default dao, eg:-dao_package=dao")
+	flag.StringVar(&dtoPackageName, "dto_package", "dto", "package name default dao, eg:-dto_package=dao")
+}
+
+func main() {
+	initConfig("go-orm-config.json", true, false)
+	// 解析输入
+	initFlag()
+	if configFile != "" {
+		initConfig(configFile, false, true)
+	}
+	// 初始化属性
+	config := initProperties()
+	err := config.Generator()
+	if err != nil {
+		logger.FatalF("generate fail,err=%s", err)
+	}
+	fmt.Println("generate template finished")
+}
+
+func GetRootPath() string {
+	curFilename := os.Args[0]
+	binaryPath, err := exec.LookPath(curFilename)
+	if err != nil {
+		panic(err)
+	}
+	binaryPath, err = filepath.Abs(binaryPath)
+	if err != nil {
+		panic(err)
+	}
+	return filepath.Dir(binaryPath)
+}
+
+// 是否忽略error
+func initConfig(fileName string, fail, asb bool) {
+	if !asb {
+		fileName = filepath.Clean(fmt.Sprintf("%s/%s", GetRootPath(), fileName))
+	}
+	file, err := os.Open(fileName)
+	fmt.Println(fmt.Sprintf("load config %s", fileName))
+	if err != nil {
+		if fail {
+			return
+		}
+		logger.FatalF("not fond config-file", err)
+	}
+	err = json.NewDecoder(file).Decode(config)
+	if err != nil {
+		bytes, _ := json.Marshal(config)
+		logger.InfoF(`
+your config should be as follow:
+%s
+`, bytes)
+		logger.FatalF("decode config-file err,err=%s", err)
+	}
+	reloadConfig()
+}
+
+// 初始化属性
+func initProperties() *orm.Config {
+	config := new(orm.Config)
+	config.DbType = orm.GetDbType(dbType)
+	config.DbName = dbName
+	config.DbUserName = dbUserName
+	config.DbPassword = dbPassword
+	config.DbHost = dbHost
+	config.DbPort = dbPort
+	config.DbCharset = dbCharset
+	config.GeneratorModel = true
+	config.GeneratorDao = true
+	config.GeneratorDto = true
+	config.DaoPackageName = daoPackageName
+	config.ModelPackageName = modelPackageName
+	config.DtoPackageName = dtoPackageName
+	config.Tags = addTag(tags)
+	config.SaveFile = destDir
+	config.TableNames = tableNames
+	return config
+}
+
+// 指示
+func initFlag() {
+	flag.Parse()
+	if help {
+		fmt.Println(`generator version: anthony/1.0.0
+Usage: generator -host=localhost -port=3306 -d=urban_v -u=root -p=123456 -t=class -t=student -tag=xorm -dir=./tmp
+Option:`)
+		flag.PrintDefaults()
+		os.Exit(successExit)
+	}
+
+	if showVersion {
+		fmt.Printf("generator version: anthony/%s", generatorVersion)
+		os.Exit(successExit)
+	}
+}
+
+func addTag(tags strFlags) []string {
+	str := "xorm"
+	result := make([]string, 0)
+	result = append(result, str)
+	for _, e := range tags {
+		if strings.Compare(str, e) == 0 {
+			continue
+		}
+		result = append(result, e)
+	}
+	return result
+}
+
+func reloadConfig() {
+	dbType = config.DbType
+	tags = config.Tags
+	dbName = config.DbName
+	dbHost = config.DbHost
+	dbPort = config.DbPort
+	dbUserName = config.DbUserName
+	dbPassword = config.DbPassword
+	dbCharset = config.DbCharset
+}
+
+type DBConfig struct {
+	DbType     string   `json:"db_type"`
+	Tags       strFlags `json:"tags"`
+	DbName     string   `json:"db_name"`
+	DbHost     string   `json:"db_host"`
+	DbPort     int      `json:"db_port"`
+	DbUserName string   `json:"db_user_name"`
+	DbPassword string   `json:"db_password"`
+	DbCharset  string   `json:"db_charset"`
+}

+ 45 - 0
file/file.go

@@ -0,0 +1,45 @@
+package file
+
+import (
+	"os"
+	"path/filepath"
+)
+
+func FileExist(filename string) bool {
+	_, err := os.Stat(filename)
+	return err == nil || os.IsExist(err)
+}
+
+func GetFilfAbsPath(fileName string) (string, error) {
+	return filepath.Abs(fileName)
+}
+
+func WriteFile(saveFilePath string, body []byte) error {
+	dir := filepath.Dir(saveFilePath)
+	e := mkdir(dir)
+	if e != nil {
+		return e
+	}
+	file, err := os.OpenFile(saveFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+	_, err = file.Write(body)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func mkdir(dir string) error {
+	if _, err := os.Stat(dir); err != nil {
+		if os.IsNotExist(err) {
+			err := os.MkdirAll(dir, os.ModePerm)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}

+ 192 - 0
file/package.go

@@ -0,0 +1,192 @@
+package file
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+func NewTemplate(dir, old, new string) error {
+	return ReplacePackage(dir, old, new, []string{"vendor", ".git", ".idea"})
+}
+
+func ReplacePackageV2(dir string, old []string, ignoreFile []string) error {
+	if old == nil || len(old) == 0 {
+		return nil
+	}
+	files, err := GetAllFiles(dir, func(fileName string) bool {
+		var pass = true
+		if ignoreFile == nil || len(ignoreFile) == 0 {
+			return true
+		}
+		for _, elem := range ignoreFile {
+			if strings.Contains(fileName, elem) {
+				pass = false
+				break
+			}
+		}
+		return pass
+	})
+
+	if err != nil {
+		return err
+	}
+	for _, file := range files {
+		err := ModifyFileV2(old, file)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func ReplacePackage(dir, old, new string, ignoreFile []string) error {
+	files, err := GetAllFiles(dir, func(fileName string) bool {
+		var pass = true
+		if ignoreFile == nil || len(ignoreFile) == 0 {
+			return true
+		}
+		for _, elem := range ignoreFile {
+			if strings.Contains(fileName, elem) {
+				pass = false
+				break
+			}
+		}
+		return pass
+	})
+
+	if err != nil {
+		return err
+	}
+	for _, file := range files {
+		err := ModifyFile(old, new, file)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+type FilterFile func(fileName string) bool
+
+func GetAllFiles(dirPth string, filter FilterFile) ([]string, error) {
+	files := make([]string, 0)
+	err := filepath.Walk(dirPth, func(path string, info os.FileInfo, err error) error {
+		if info.IsDir() {
+			return nil
+		}
+		if filter(path) {
+			files = append(files, path)
+		}
+		return nil
+	})
+	if err != nil {
+		return nil, err
+	}
+	return files, nil
+}
+
+func ModifyFile(old, new string, fileName string) error {
+	rfile, err := os.OpenFile(fileName, os.O_RDONLY, os.ModePerm)
+	if err != nil {
+		return err
+	}
+	defer rfile.Close()
+	reader := bufio.NewReader(rfile)
+	fileLine := make([]string, 0)
+	count := 0
+	for {
+		lines, isEOF, err := reader.ReadLine()
+		if err != nil {
+			if err == io.EOF {
+				break
+			}
+			return err
+		}
+		if isEOF {
+			break
+		}
+
+		line := string(lines)
+		if strings.Contains(line, old) {
+			count++
+			fileLine = append(fileLine, strings.ReplaceAll(string(lines), old, new))
+		} else {
+			fileLine = append(fileLine, line)
+		}
+	}
+	if count == 0 {
+		return nil
+	}
+	wfile, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC, os.ModePerm)
+	if err != nil {
+		return err
+	}
+	defer wfile.Close()
+	for _, elem := range fileLine {
+		_, err := fmt.Fprintln(wfile, elem)
+		if err != nil {
+			return err
+		}
+	}
+	fmt.Printf("change success : %s\n", fileName)
+	return nil
+}
+
+func ModifyFileV2(old []string, fileName string) error {
+	rfile, err := os.OpenFile(fileName, os.O_RDONLY, os.ModePerm)
+	if err != nil {
+		return err
+	}
+	defer rfile.Close()
+	reader := bufio.NewReader(rfile)
+	fileLine := make([]string, 0)
+	count := 0
+	for {
+		lines, isEOF, err := reader.ReadLine()
+		if err != nil {
+			if strings.Compare(err.Error(), "EOF") == 0 {
+				break
+			}
+			return err
+		}
+		if isEOF {
+			break
+		}
+
+		line := string(lines)
+		for _, elem := range old {
+			if strings.Contains(line, elem) {
+				count++
+				rep := make([]byte, len(elem))
+				for i, _ := range rep {
+					rep[i] = '*'
+				}
+				fileLine = append(fileLine, strings.ReplaceAll(string(lines), elem, string(rep)))
+				break
+			} else {
+				fileLine = append(fileLine, line)
+				break
+			}
+		}
+	}
+	if count == 0 {
+		return nil
+	}
+	wfile, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC, os.ModePerm)
+	if err != nil {
+		return err
+	}
+	defer wfile.Close()
+	for _, elem := range fileLine {
+		_, err := fmt.Fprintln(wfile, elem)
+		if err != nil {
+			return err
+		}
+	}
+	fmt.Printf("replace %s success!!!\n", fileName)
+	return nil
+}

+ 64 - 0
file/pwd.go

@@ -0,0 +1,64 @@
+package file
+
+import (
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+// 当前执行脚本路径
+func GetCurPath() string {
+	path, _ := exec.LookPath(os.Args[0])
+	base := filepath.Base(path)
+	path, _ = filepath.Abs(path)
+	return strings.TrimSuffix(path, "/"+base)
+}
+
+// 项目根路径,携带有项目名称
+func GetRootPath() string {
+	dir, _ := os.Getwd()
+	return dir
+}
+
+func GetFilePath(fileName string) (string, error) {
+	// 当前执行脚本路径
+	var curPath = func() string {
+		path, _ := exec.LookPath(os.Args[0])
+		path, _ = filepath.Abs(path)
+		return filepath.Dir(path)
+	}
+
+	// 项目根路径,携带有项目名称
+	var rootPath = func() string {
+		path, _ := os.Getwd()
+		return path
+	}
+
+	// 判断文件是否存在
+	var exist = func(filename string) bool {
+		_, err := os.Stat(filename)
+		return err == nil || os.IsExist(err)
+	}
+
+	str, _ := filepath.Abs(fileName)
+	if exist(str) {
+		return str, nil
+	}
+	if strings.HasPrefix(fileName, "/") {
+		if !exist(fileName) {
+			return "", fmt.Errorf("%s not fond", fileName)
+		}
+		return fileName, nil
+	}
+	path1 := filepath.Clean(fmt.Sprintf("%s/%s", rootPath(), fileName))
+	if !exist(path1) {
+		path2 := filepath.Clean(fmt.Sprintf("%s/%s", curPath(), fileName))
+		if !exist(path2) {
+			return "", fmt.Errorf("%s and %s not fond", path1, path2)
+		}
+		return path2, nil
+	}
+	return path1, nil
+}

+ 5 - 0
git-hook.sh

@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -e
+
+curl -O https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/new-githook.shell && bash new-githook.shell && rm new-githook.shell

+ 10 - 0
go.mod

@@ -0,0 +1,10 @@
+module gitea.ckfah.com/go-script
+
+go 1.13
+
+require (
+	github.com/go-sql-driver/mysql v1.5.0
+	github.com/go-xorm/xorm v0.7.9
+	github.com/kisielk/errcheck v1.4.0
+	golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f
+)

+ 159 - 0
go.sum

@@ -0,0 +1,159 @@
+cloud.google.com/go v0.26.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.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
+github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0=
+github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
+github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+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/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+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/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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
+github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.4.0 h1:ueN6QYA+c7eDQo7ebpNdYR8mUJZThiGz9PEoJEMGPzA=
+github.com/kisielk/errcheck v1.4.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+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/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+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/lint v0.0.0-20190301231843-5614ed5bae6f h1:hX65Cu3JDlGH3uEdK7I99Ii+9kjD6mvnnpfLdEAH0x4=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+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-20181114220301-adae6a3d119a/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-20190125091013-d26f9f9a57f3/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+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-20190911185100-cd5d95a43a6e/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-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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/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-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-20190312170243-e65039ee4138 h1:H3uGjxCR/6Ds0Mjgyp7LMK81+LvmbvWWEnJhzk1Pi9E=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f h1:tuwaIjfUa6eI6REiNueIxvNm1popyPUnqWga83S7U0o=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+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/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+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=
+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=
+xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
+xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
+xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb h1:msX3zG3BPl8Ti+LDzP33/9K7BzO/WqFXk610K1kYKfo=
+xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=

+ 22 - 0
internal/AppendSliceValue.go

@@ -0,0 +1,22 @@
+package bindata
+
+import "strings"
+
+// borrowed from https://github.com/hashicorp/serf/blob/master/command/agent/flag_slice_value.go
+
+// AppendSliceValue implements the flag.Value interface and allows multiple
+// calls to the same variable to append a list.
+type AppendSliceValue []string
+
+func (s *AppendSliceValue) String() string {
+	return strings.Join(*s, ",")
+}
+
+func (s *AppendSliceValue) Set(value string) error {
+	if *s == nil {
+		*s = make([]string, 0, 1)
+	}
+
+	*s = append(*s, value)
+	return nil
+}

+ 201 - 0
internal/README.md

@@ -0,0 +1,201 @@
+## bindata
+[![Build Status](https://cloud.drone.io/api/badges/go-bindata/go-bindata/status.svg)](https://cloud.drone.io/go-bindata/go-bindata)
+[![Go Report Card](https://goreportcard.com/badge/github.com/go-bindata/bindata)](https://goreportcard.com/report/github.com/go-bindata/bindata)
+
+This package converts any file into manageable Go source code. Useful for
+embedding binary data into a go program. The file data is optionally gzip
+compressed before being converted to a raw byte slice.
+
+It comes with a command line tool in the `go-bindata` sub directory.
+This tool offers a set of command line options, used to customize the
+output being generated.
+
+
+### Installation
+
+To install the library and command line program, use the following:
+
+	go get -u github.com/go-bindata/go-bindata/...
+
+
+### Usage
+
+Conversion is done on one or more sets of files. They are all embedded in a new
+Go source file, along with a table of contents and an `Asset` function,
+which allows quick access to the asset, based on its name.
+
+The simplest invocation generates a `bindata.go` file in the current
+working directory. It includes all assets from the `data` directory.
+
+	$ go-bindata data/
+
+To include all input sub-directories recursively, use the ellipsis postfix
+as defined for Go import paths. Otherwise it will only consider assets in the
+input directory itself.
+
+	$ go-bindata data/...
+
+To specify the name of the output file being generated, we use the following:
+
+	$ go-bindata -o myfile.go data/
+
+Multiple input directories can be specified if necessary.
+
+	$ go-bindata dir1/... /path/to/dir2/... dir3
+
+
+The following paragraphs detail some of the command line options which can be 
+supplied to `go-bindata`. Refer to the `testdata/out` directory for various
+output examples from the assets in `testdata/in`. Each example uses different
+command line options.
+
+To ignore files, pass in regexes using -ignore, for example:
+
+    $ go-bindata -ignore=\\.gitignore data/...
+
+### Accessing an asset
+
+To access asset data, we use the `Asset(string) ([]byte, error)` function which
+is included in the generated output.
+
+```go
+data, err := Asset("pub/style/foo.css")
+if err != nil {
+	// Asset was not found.
+}
+
+// use asset data
+```
+
+
+### Debug vs Release builds
+
+When invoking the program with the `-debug` flag, the generated code does
+not actually include the asset data. Instead, it generates function stubs
+which load the data from the original file on disk. The asset API remains
+identical between debug and release builds, so your code will not have to
+change.
+
+This is useful during development when you expect the assets to change often.
+The host application using these assets uses the same API in both cases and
+will not have to care where the actual data comes from.
+
+An example is a Go webserver with some embedded, static web content like
+HTML, JS and CSS files. While developing it, you do not want to rebuild the
+whole server and restart it every time you make a change to a bit of
+javascript. You just want to build and launch the server once. Then just press
+refresh in the browser to see those changes. Embedding the assets with the
+`debug` flag allows you to do just that. When you are finished developing and
+ready for deployment, just re-invoke `go-bindata` without the `-debug` flag.
+It will now embed the latest version of the assets.
+
+
+### Lower memory footprint
+
+Using the `-nomemcopy` flag, will alter the way the output file is generated.
+It will employ a hack that allows us to read the file data directly from
+the compiled program's `.rodata` section. This ensures that when we
+call our generated function, we omit unnecessary memcopies.
+
+The downside of this, is that it requires dependencies on the `reflect` and
+`unsafe` packages. These may be restricted on platforms like AppEngine and
+thus prevent you from using this mode.
+
+Another disadvantage is that the byte slice we create, is strictly read-only.
+For most use-cases this is not a problem, but if you ever try to alter the
+returned byte slice, a runtime panic is thrown. Use this mode only on target
+platforms where memory constraints are an issue.
+
+The default behaviour is to use the old code generation method. This
+prevents the two previously mentioned issues, but will employ at least one
+extra memcopy and thus increase memory requirements.
+
+For instance, consider the following two examples:
+
+This would be the default mode, using an extra memcopy but gives a safe
+implementation without dependencies on `reflect` and `unsafe`:
+
+```go
+func myfile() []byte {
+    return []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a}
+}
+```
+
+Here is the same functionality, but uses the `.rodata` hack.
+The byte slice returned from this example can not be written to without
+generating a runtime error.
+
+```go
+var _myfile = "\x89\x50\x4e\x47\x0d\x0a\x1a"
+
+func myfile() []byte {
+    var empty [0]byte
+    sx := (*reflect.StringHeader)(unsafe.Pointer(&_myfile))
+    b := empty[:]
+    bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+    bx.Data = sx.Data
+    bx.Len = len(_myfile)
+    bx.Cap = bx.Len
+    return b
+}
+```
+
+
+### Optional compression
+
+When the `-nocompress` flag is given, the supplied resource is *not* GZIP
+compressed before being turned into Go code. The data should still be accessed
+through a function call, so nothing changes in the usage of the generated file.
+
+This feature is useful if you do not care for compression, or the supplied
+resource is already compressed. Doing it again would not add any value and may
+even increase the size of the data.
+
+The default behaviour of the program is to use compression.
+
+
+### Path prefix stripping
+
+The keys used in the `_bindata` map, are the same as the input file name
+passed to `go-bindata`. This includes the path. In most cases, this is not
+desirable, as it puts potentially sensitive information in your code base.
+For this purpose, the tool supplies another command line flag `-prefix`.
+This accepts a portion of a path name, which should be stripped off from
+the map keys and function names.
+
+For example, running without the `-prefix` flag, we get:
+
+	$ go-bindata /path/to/templates/
+
+	_bindata["/path/to/templates/foo.html"] = path_to_templates_foo_html
+
+Running with the `-prefix` flag, we get:
+
+	$ go-bindata -prefix "/path/to/" /path/to/templates/
+
+	_bindata["templates/foo.html"] = templates_foo_html
+
+
+### Build tags
+
+With the optional `-tags` flag, you can specify any go build tags that
+must be fulfilled for the output file to be included in a build. This
+is useful when including binary data in multiple formats, where the desired
+format is specified at build time with the appropriate tags.
+
+The tags are appended to a `// +build` line in the beginning of the output file
+and must follow the build tags syntax specified by the go tool.
+
+### Serve assets with `net/http`
+
+With the `-fs` flag, `go-bindata` will add an `AssetFile()` method returning an `http.FileSystem` interface:
+
+	$ go-bindata -fs -prefix "static/" static/
+
+Use `-prefix` flag to strip first level dir, then in your `net/http` router, you can use `AssetFile()` with `http.FileServer()` like:
+
+```go
+mux := http.NewServeMux()
+mux.Handle("/static", http.FileServer(AssetFile()))
+http.ListenAndServe(":8080", mux)
+```

+ 12 - 0
internal/asset.go

@@ -0,0 +1,12 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package bindata
+
+// Asset holds information about a single asset to be processed.
+type Asset struct {
+	Path string // Full file path.
+	Name string // Key used in TOC -- name by which asset is referenced.
+	Func string // Function name for the procedure returning the asset contents.
+}

+ 44 - 0
internal/bytewriter.go

@@ -0,0 +1,44 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package bindata
+
+import (
+	"fmt"
+	"io"
+)
+
+var (
+	newline    = []byte{'\n'}
+	dataindent = []byte{'\t', '\t'}
+	space      = []byte{' '}
+)
+
+type ByteWriter struct {
+	io.Writer
+	c int
+}
+
+func (w *ByteWriter) Write(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return
+	}
+
+	for n = range p {
+		if w.c%12 == 0 {
+			w.Writer.Write(newline)
+			w.Writer.Write(dataindent)
+			w.c = 0
+		} else {
+			w.Writer.Write(space)
+		}
+
+		fmt.Fprintf(w.Writer, "0x%02x,", p[n])
+		w.c++
+	}
+
+	n++
+
+	return
+}

+ 209 - 0
internal/config.go

@@ -0,0 +1,209 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package bindata
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"regexp"
+)
+
+// InputConfig defines options on a asset directory to be convert.
+type InputConfig struct {
+	// Path defines a directory containing asset files to be included
+	// in the generated output.
+	Path string
+
+	// Recursive defines whether subdirectories of Path
+	// should be recursively included in the conversion.
+	Recursive bool
+}
+
+// Config defines a set of options for the asset conversion.
+type Config struct {
+	// Name of the package to use. Defaults to 'main'.
+	Package string
+
+	// Tags specify a set of optional build tags, which should be
+	// included in the generated output. The tags are appended to a
+	// `// +build` line in the beginning of the output file
+	// and must follow the build tags syntax specified by the go tool.
+	Tags string
+
+	// Input defines the directory path, containing all asset files as
+	// well as whether to recursively process assets in any sub directories.
+	Input []InputConfig
+
+	// Output defines the output file for the generated code.
+	// If left empty, this defaults to 'bindata.go' in the current
+	// working directory.
+	Output string
+
+	// Prefix defines a path prefix which should be stripped from all
+	// file names when generating the keys in the table of contents.
+	// For example, running without the `-prefix` flag, we get:
+	//
+	// 	$ go-bindata /path/to/templates
+	// 	go_bindata["/path/to/templates/foo.html"] = _path_to_templates_foo_html
+	//
+	// Running with the `-prefix` flag, we get:
+	//
+	// 	$ go-bindata -prefix "/path/to/" /path/to/templates/foo.html
+	// 	go_bindata["templates/foo.html"] = templates_foo_html
+	Prefix string
+
+	// NoMemCopy will alter the way the output file is generated.
+	//
+	// It will employ a hack that allows us to read the file data directly from
+	// the compiled program's `.rodata` section. This ensures that when we call
+	// call our generated function, we omit unnecessary mem copies.
+	//
+	// The downside of this, is that it requires dependencies on the `reflect` and
+	// `unsafe` packages. These may be restricted on platforms like AppEngine and
+	// thus prevent you from using this mode.
+	//
+	// Another disadvantage is that the byte slice we create, is strictly read-only.
+	// For most use-cases this is not a problem, but if you ever try to alter the
+	// returned byte slice, a runtime panic is thrown. Use this mode only on target
+	// platforms where memory constraints are an issue.
+	//
+	// The default behaviour is to use the old code generation method. This
+	// prevents the two previously mentioned issues, but will employ at least one
+	// extra memcopy and thus increase memory requirements.
+	//
+	// For instance, consider the following two examples:
+	//
+	// This would be the default mode, using an extra memcopy but gives a safe
+	// implementation without dependencies on `reflect` and `unsafe`:
+	//
+	// 	func myfile() []byte {
+	// 		return []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a}
+	// 	}
+	//
+	// Here is the same functionality, but uses the `.rodata` hack.
+	// The byte slice returned from this example can not be written to without
+	// generating a runtime error.
+	//
+	// 	var _myfile = "\x89\x50\x4e\x47\x0d\x0a\x1a"
+	//
+	// 	func myfile() []byte {
+	// 		var empty [0]byte
+	// 		sx := (*reflect.StringHeader)(unsafe.Pointer(&_myfile))
+	// 		b := empty[:]
+	// 		bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+	// 		bx.Data = sx.Data
+	// 		bx.Len = len(_myfile)
+	// 		bx.Cap = bx.Len
+	// 		return b
+	// 	}
+	NoMemCopy bool
+
+	// NoCompress means the assets are /not/ GZIP compressed before being turned
+	// into Go code. The generated function will automatically unzip
+	// the file data when called. Defaults to false.
+	NoCompress bool
+
+	// HttpFileSystem means whether generate return http.FileSystem interface
+	// instance's function.When true,will generate relate code.
+	HttpFileSystem bool
+
+	// Perform a debug build. This generates an asset file, which
+	// loads the asset contents directly from disk at their original
+	// location, instead of embedding the contents in the code.
+	//
+	// This is mostly useful if you anticipate that the assets are
+	// going to change during your development cycle. You will always
+	// want your code to access the latest version of the asset.
+	// Only in release mode, will the assets actually be embedded
+	// in the code. The default behaviour is Release mode.
+	Debug bool
+
+	// Perform a dev build, which is nearly identical to the debug option. The
+	// only difference is that instead of absolute file paths in generated code,
+	// it expects a variable, `rootDir`, to be set in the generated code's
+	// package (the author needs to do this manually), which it then prepends to
+	// an asset's name to construct the file path on disk.
+	//
+	// This is mainly so you can push the generated code file to a shared
+	// repository.
+	Dev bool
+
+	// When true, size, mode and modtime are not preserved from files
+	NoMetadata bool
+	// When nonzero, use this as mode for all files.
+	Mode uint
+	// When nonzero, use this as unix timestamp for all files.
+	ModTime int64
+
+	// Ignores any filenames matching the regex pattern specified, e.g.
+	// path/to/file.ext will ignore only that file, or \\.gitignore
+	// will match any .gitignore file.
+	//
+	// This parameter can be provided multiple times.
+	Ignore []*regexp.Regexp
+}
+
+// NewConfig returns a default configuration struct.
+func NewConfig() *Config {
+	c := new(Config)
+	c.Package = "main"
+	c.NoMemCopy = false
+	c.NoCompress = false
+	c.HttpFileSystem = false
+	c.Debug = false
+	c.Output = "./bindata.go"
+	c.Ignore = make([]*regexp.Regexp, 0)
+	return c
+}
+
+// validate ensures the config has sane values.
+// Part of which means checking if certain file/directory paths exist.
+func (c *Config) validate() error {
+	if len(c.Package) == 0 {
+		return fmt.Errorf("missing package name")
+	}
+
+	for _, input := range c.Input {
+		_, err := os.Lstat(input.Path)
+		if err != nil {
+			return fmt.Errorf("failed to stat input path '%s': %v", input.Path, err)
+		}
+	}
+
+	if len(c.Output) == 0 {
+		cwd, err := os.Getwd()
+		if err != nil {
+			return fmt.Errorf("unable to determine current working directory")
+
+		}
+
+		c.Output = filepath.Join(cwd, "bindata.go")
+	}
+
+	stat, err := os.Lstat(c.Output)
+	if err != nil {
+		if !os.IsNotExist(err) {
+			return fmt.Errorf("output path: %v", err)
+		}
+
+		// File does not exist. This is fine, just make
+		// sure the directory it is to be in exists.
+		dir, _ := filepath.Split(c.Output)
+		if dir != "" {
+			err = os.MkdirAll(dir, 0744)
+
+			if err != nil {
+				return fmt.Errorf("create output directory: %v", err)
+			}
+		}
+	}
+
+	if stat != nil && stat.IsDir() {
+		return fmt.Errorf("output path is a directory")
+	}
+
+	return nil
+}

+ 261 - 0
internal/convert.go

@@ -0,0 +1,261 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package bindata
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+	"path/filepath"
+	"regexp"
+	"sort"
+	"strings"
+	"unicode"
+)
+
+// Translate reads assets from an input directory, converts them
+// to Go code and writes new files to the output specified
+// in the given configuration.
+func Translate(c *Config) error {
+	var toc []Asset
+
+	// Ensure our configuration has sane values.
+	err := c.validate()
+	if err != nil {
+		return err
+	}
+
+	var knownFuncs = make(map[string]int)
+	var visitedPaths = make(map[string]bool)
+	// Locate all the assets.
+	for _, input := range c.Input {
+		err = findFiles(input.Path, c.Prefix, input.Recursive, &toc, c.Ignore, knownFuncs, visitedPaths)
+		if err != nil {
+			return err
+		}
+	}
+
+	// Create output file.
+	fd, err := os.Create(c.Output)
+	if err != nil {
+		return err
+	}
+
+	defer fd.Close()
+
+	// Create a buffered writer for better performance.
+	bfd := bufio.NewWriter(fd)
+	defer bfd.Flush()
+
+	// Write the header. This makes e.g. Github ignore diffs in generated files.
+	if _, err = fmt.Fprintf(bfd, "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package %s generated by go-bindata.", c.Package); err != nil {
+		return err
+	}
+	if _, err = fmt.Fprint(bfd, "// sources:\n"); err != nil {
+		return err
+	}
+
+	wd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+
+	for _, asset := range toc {
+		relative, _ := filepath.Rel(wd, asset.Path)
+		if _, err = fmt.Fprintf(bfd, "// %s\n", filepath.ToSlash(relative)); err != nil {
+			return err
+		}
+	}
+	//if _, err = fmt.Fprint(bfd, "// DO NOT EDIT!\n\n"); err != nil {
+	//	return err
+	//}
+
+	// Write build tags, if applicable.
+	if len(c.Tags) > 0 {
+		if _, err = fmt.Fprintf(bfd, "// +build %s\n\n", c.Tags); err != nil {
+			return err
+		}
+	}
+
+	// Write package declaration.
+	_, err = fmt.Fprintf(bfd, "package %s\n\n", c.Package)
+	if err != nil {
+		return err
+	}
+
+	// Write assets.
+	if c.Debug || c.Dev {
+		err = writeDebug(bfd, c, toc)
+	} else {
+		err = writeRelease(bfd, c, toc)
+	}
+
+	if err != nil {
+		return err
+	}
+
+	// Write table of contents
+	if err := writeTOC(bfd, toc); err != nil {
+		return err
+	}
+	// Write hierarchical tree of assets
+	if err := writeTOCTree(bfd, toc); err != nil {
+		return err
+	}
+
+	// Write restore procedure
+	return writeRestore(bfd)
+}
+
+// ByName implements sort.Interface for []os.FileInfo based on Name()
+type ByName []os.FileInfo
+
+func (v ByName) Len() int           { return len(v) }
+func (v ByName) Swap(i, j int)      { v[i], v[j] = v[j], v[i] }
+func (v ByName) Less(i, j int) bool { return v[i].Name() < v[j].Name() }
+
+// findFiles recursively finds all the file paths in the given directory tree.
+// They are added to the given map as keys. Values will be safe function names
+// for each file, which will be used when generating the output code.
+func findFiles(dir, prefix string, recursive bool, toc *[]Asset, ignore []*regexp.Regexp, knownFuncs map[string]int, visitedPaths map[string]bool) error {
+	dirpath := dir
+	if len(prefix) > 0 {
+		dirpath, _ = filepath.Abs(dirpath)
+		prefix, _ = filepath.Abs(prefix)
+		prefix = filepath.ToSlash(prefix)
+	}
+
+	fi, err := os.Stat(dirpath)
+	if err != nil {
+		return err
+	}
+
+	var list []os.FileInfo
+
+	if !fi.IsDir() {
+		dirpath = filepath.Dir(dirpath)
+		list = []os.FileInfo{fi}
+	} else {
+		visitedPaths[dirpath] = true
+		fd, err := os.Open(dirpath)
+		if err != nil {
+			return err
+		}
+
+		defer fd.Close()
+
+		list, err = fd.Readdir(0)
+		if err != nil {
+			return err
+		}
+
+		// Sort to make output stable between invocations
+		sort.Sort(ByName(list))
+	}
+
+	for _, file := range list {
+		var asset Asset
+		asset.Path = filepath.Join(dirpath, file.Name())
+		asset.Name = filepath.ToSlash(asset.Path)
+
+		ignoring := false
+		for _, re := range ignore {
+			if re.MatchString(asset.Path) {
+				ignoring = true
+				break
+			}
+		}
+		if ignoring {
+			continue
+		}
+
+		if file.IsDir() {
+			if recursive {
+				recursivePath := filepath.Join(dir, file.Name())
+				visitedPaths[asset.Path] = true
+				findFiles(recursivePath, prefix, recursive, toc, ignore, knownFuncs, visitedPaths)
+			}
+			continue
+		} else if file.Mode()&os.ModeSymlink == os.ModeSymlink {
+			var linkPath string
+			if linkPath, err = os.Readlink(asset.Path); err != nil {
+				return err
+			}
+			if !filepath.IsAbs(linkPath) {
+				if linkPath, err = filepath.Abs(dirpath + "/" + linkPath); err != nil {
+					return err
+				}
+			}
+			if _, ok := visitedPaths[linkPath]; !ok {
+				visitedPaths[linkPath] = true
+				findFiles(asset.Path, prefix, recursive, toc, ignore, knownFuncs, visitedPaths)
+			}
+			continue
+		}
+
+		if strings.HasPrefix(asset.Name, prefix) {
+			asset.Name = asset.Name[len(prefix):]
+		} else {
+			asset.Name = filepath.Join(dir, file.Name())
+		}
+
+		// If we have a leading slash, get rid of it.
+		if len(asset.Name) > 0 && asset.Name[0] == '/' {
+			asset.Name = asset.Name[1:]
+		}
+
+		// This shouldn't happen.
+		if len(asset.Name) == 0 {
+			return fmt.Errorf("invalid file: %v", asset.Path)
+		}
+
+		asset.Func = safeFunctionName(asset.Name, knownFuncs)
+		asset.Path, _ = filepath.Abs(asset.Path)
+		*toc = append(*toc, asset)
+	}
+
+	return nil
+}
+
+var regFuncName = regexp.MustCompile(`[^a-zA-Z0-9_]`)
+
+// safeFunctionName converts the given name into a name
+// which qualifies as a valid function identifier. It
+// also compares against a known list of functions to
+// prevent conflict based on name translation.
+func safeFunctionName(name string, knownFuncs map[string]int) string {
+	var inBytes, outBytes []byte
+	var toUpper bool
+
+	name = strings.ToLower(name)
+	inBytes = []byte(name)
+
+	for i := 0; i < len(inBytes); i++ {
+		if regFuncName.Match([]byte{inBytes[i]}) {
+			toUpper = true
+		} else if toUpper {
+			outBytes = append(outBytes, []byte(strings.ToUpper(string(inBytes[i])))...)
+			toUpper = false
+		} else {
+			outBytes = append(outBytes, inBytes[i])
+		}
+	}
+
+	name = string(outBytes)
+
+	// Identifier can't start with a digit.
+	if unicode.IsDigit(rune(name[0])) {
+		name = "_" + name
+	}
+
+	if num, ok := knownFuncs[name]; ok {
+		knownFuncs[name] = num + 1
+		name = fmt.Sprintf("%s%d", name, num)
+	} else {
+		knownFuncs[name] = 2
+	}
+
+	return name
+}

+ 146 - 0
internal/debug.go

@@ -0,0 +1,146 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package bindata
+
+import (
+	"fmt"
+	"io"
+)
+
+// writeDebug writes the debug code file.
+func writeDebug(w io.Writer, c *Config, toc []Asset) error {
+	err := writeDebugHeader(w, c)
+	if err != nil {
+		return err
+	}
+
+	err = writeAssetFS(w, c)
+	if err != nil {
+		return err
+	}
+
+	for i := range toc {
+		err = writeDebugAsset(w, c, &toc[i])
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// writeDebugHeader writes output file headers.
+// This targets debug builds.
+func writeDebugHeader(w io.Writer, c *Config) error {
+	var header string
+
+	if c.HttpFileSystem {
+		header = `import (
+	"bytes"
+	"net/http"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"`
+	} else {
+		header = `import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"`
+	}
+
+	_, err := fmt.Fprintf(w, `%s
+)
+
+// bindataRead reads the given file from disk. It returns an error on failure.
+func bindataRead(path, name string) ([]byte, error) {
+	buf, err := ioutil.ReadFile(path)
+	if err != nil {
+		err = fmt.Errorf("Error reading asset %%s at %%s: %%v", name, path, err)
+	}
+	return buf, err
+}
+
+type asset struct {
+	bytes []byte
+	info  os.FileInfo
+}
+
+type bindataFileInfo struct {
+	name    string
+	size    int64
+	mode    os.FileMode
+	modTime time.Time
+}
+
+// Name return file name
+func (fi bindataFileInfo) Name() string {
+	return fi.name
+}
+
+// Size return file size
+func (fi bindataFileInfo) Size() int64 {
+	return fi.size
+}
+
+// Mode return file mode
+func (fi bindataFileInfo) Mode() os.FileMode {
+	return fi.mode
+}
+
+// ModTime return file modify time
+func (fi bindataFileInfo) ModTime() time.Time {
+	return fi.modTime
+}
+
+// IsDir return file whether a directory
+func (fi bindataFileInfo) IsDir() bool {
+	return fi.mode&os.ModeDir != 0
+}
+
+// Sys return file is sys mode
+func (fi bindataFileInfo) Sys() interface{} {
+	return nil
+}
+
+`, header)
+	return err
+}
+
+// writeDebugAsset write a debug entry for the given asset.
+// A debug entry is simply a function which reads the asset from
+// the original file (e.g.: from disk).
+func writeDebugAsset(w io.Writer, c *Config, asset *Asset) error {
+	pathExpr := fmt.Sprintf("%q", asset.Path)
+	if c.Dev {
+		pathExpr = fmt.Sprintf("filepath.Join(rootDir, %q)", asset.Name)
+	}
+
+	_, err := fmt.Fprintf(w, `// %s reads file data from disk. It returns an error on failure.
+func %s() (*asset, error) {
+	path := %s
+	name := %q
+	bytes, err := bindataRead(path, name)
+	if err != nil {
+		return nil, err
+	}
+
+	fi, err := os.Stat(path)
+	if err != nil {
+		err = fmt.Errorf("Error reading asset info %%s at %%s: %%v", name, path, err)
+	}
+
+	a := &asset{bytes: bytes, info: fi}
+	return a, err
+}
+
+`, asset.Func, asset.Func, pathExpr, asset.Name)
+	return err
+}

+ 129 - 0
internal/doc.go

@@ -0,0 +1,129 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+/*
+bindata converts any file into manageable Go source code. Useful for
+embedding binary data into a go program. The file data is optionally gzip
+compressed before being converted to a raw byte slice.
+
+The following paragraphs cover some of the customization options
+which can be specified in the Config struct, which must be passed into
+the Translate() call.
+
+
+Debug vs Release builds
+
+When used with the `Debug` option, the generated code does not actually include
+the asset data. Instead, it generates function stubs which load the data from
+the original file on disk. The asset API remains identical between debug and
+release builds, so your code will not have to change.
+
+This is useful during development when you expect the assets to change often.
+The host application using these assets uses the same API in both cases and
+will not have to care where the actual data comes from.
+
+An example is a Go webserver with some embedded, static web content like
+HTML, JS and CSS files. While developing it, you do not want to rebuild the
+whole server and restart it every time you make a change to a bit of
+javascript. You just want to build and launch the server once. Then just press
+refresh in the browser to see those changes. Embedding the assets with the
+`debug` flag allows you to do just that. When you are finished developing and
+ready for deployment, just re-invoke `go-bindata` without the `-debug` flag.
+It will now embed the latest version of the assets.
+
+
+Lower memory footprint
+
+The `NoMemCopy` option will alter the way the output file is generated.
+It will employ a hack that allows us to read the file data directly from
+the compiled program's `.rodata` section. This ensures that when we call
+call our generated function, we omit unnecessary memcopies.
+
+The downside of this, is that it requires dependencies on the `reflect` and
+`unsafe` packages. These may be restricted on platforms like AppEngine and
+thus prevent you from using this mode.
+
+Another disadvantage is that the byte slice we create, is strictly read-only.
+For most use-cases this is not a problem, but if you ever try to alter the
+returned byte slice, a runtime panic is thrown. Use this mode only on target
+platforms where memory constraints are an issue.
+
+The default behaviour is to use the old code generation method. This
+prevents the two previously mentioned issues, but will employ at least one
+extra memcopy and thus increase memory requirements.
+
+For instance, consider the following two examples:
+
+This would be the default mode, using an extra memcopy but gives a safe
+implementation without dependencies on `reflect` and `unsafe`:
+
+	func myfile() []byte {
+		return []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a}
+	}
+
+Here is the same functionality, but uses the `.rodata` hack.
+The byte slice returned from this example can not be written to without
+generating a runtime error.
+
+	var _myfile = "\x89\x50\x4e\x47\x0d\x0a\x1a"
+
+	func myfile() []byte {
+		var empty [0]byte
+		sx := (*reflect.StringHeader)(unsafe.Pointer(&_myfile))
+		b := empty[:]
+		bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+		bx.Data = sx.Data
+		bx.Len = len(_myfile)
+		bx.Cap = bx.Len
+		return b
+	}
+
+
+Optional compression
+
+The NoCompress option indicates that the supplied assets are *not* GZIP
+compressed before being turned into Go code. The data should still be accessed
+through a function call, so nothing changes in the API.
+
+This feature is useful if you do not care for compression, or the supplied
+resource is already compressed. Doing it again would not add any value and may
+even increase the size of the data.
+
+The default behaviour of the program is to use compression.
+
+
+Path prefix stripping
+
+The keys used in the `_bindata` map are the same as the input file name
+passed to `go-bindata`. This includes the path. In most cases, this is not
+desirable, as it puts potentially sensitive information in your code base.
+For this purpose, the tool supplies another command line flag `-prefix`.
+This accepts a portion of a path name, which should be stripped off from
+the map keys and function names.
+
+For example, running without the `-prefix` flag, we get:
+
+	$ go-bindata /path/to/templates/
+
+	_bindata["/path/to/templates/foo.html"] = path_to_templates_foo_html
+
+Running with the `-prefix` flag, we get:
+
+	$ go-bindata -prefix "/path/to/" /path/to/templates/
+
+	_bindata["templates/foo.html"] = templates_foo_html
+
+
+Build tags
+
+With the optional Tags field, you can specify any go build tags that
+must be fulfilled for the output file to be included in a build. This
+is useful when including binary data in multiple formats, where the desired
+format is specified at build time with the appropriate tags.
+
+The tags are appended to a `// +build` line in the beginning of the output file
+and must follow the build tags syntax specified by the go tool.
+
+*/
+package bindata

+ 102 - 0
internal/file.go

@@ -0,0 +1,102 @@
+package bindata
+
+import (
+	"fmt"
+	"io"
+)
+
+func writeAssetFS(w io.Writer, c *Config) error {
+	if !c.HttpFileSystem {
+		return nil
+	}
+
+	_, err := fmt.Fprintf(w, `
+type assetFile struct {
+	*bytes.Reader
+	name            string
+	childInfos      []os.FileInfo
+	childInfoOffset int
+}
+
+type assetOperator struct{}
+
+// Open implement http.FileSystem interface
+func (f *assetOperator) Open(name string) (http.File, error) {
+	var err error
+	if len(name) > 0 && name[0] == '/' {
+		name = name[1:]
+	}
+	content, err := Asset(name)
+	if err == nil {
+		return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil
+	}
+	children, err := AssetDir(name)
+	if err == nil {
+		childInfos := make([]os.FileInfo, 0, len(children))
+		for _, child := range children {
+			childPath := filepath.Join(name, child)
+			info, errInfo := AssetInfo(filepath.Join(name, child))
+			if errInfo == nil {
+				childInfos = append(childInfos, info)
+			} else {
+				childInfos = append(childInfos, newDirFileInfo(childPath))
+			}
+		}
+		return &assetFile{name: name, childInfos: childInfos}, nil
+	} else {
+		// If the error is not found, return an error that will
+		// result in a 404 error. Otherwise the server returns
+		// a 500 error for files not found.
+		if strings.Contains(err.Error(), "not found") {
+			return nil, os.ErrNotExist
+		}
+		return nil, err
+	}
+}
+
+// Close no need do anything
+func (f *assetFile) Close() error {
+	return nil
+}
+
+// Readdir read dir's children file info
+func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {
+	if len(f.childInfos) == 0 {
+		return nil, os.ErrNotExist
+	}
+	if count <= 0 {
+		return f.childInfos, nil
+	}
+	if f.childInfoOffset+count > len(f.childInfos) {
+		count = len(f.childInfos) - f.childInfoOffset
+	}
+	offset := f.childInfoOffset
+	f.childInfoOffset += count
+	return f.childInfos[offset : offset+count], nil
+}
+
+// Stat read file info from asset item
+func (f *assetFile) Stat() (os.FileInfo, error) {
+	if len(f.childInfos) != 0 {
+		return newDirFileInfo(f.name), nil
+	}
+	return AssetInfo(f.name)
+}
+
+// newDirFileInfo return default dir file info
+func newDirFileInfo(name string) os.FileInfo {
+	return &bindataFileInfo{
+		name:    name,
+		size:    0,
+		mode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir
+		modTime: time.Time{}}
+}
+
+// AssetFile return a http.FileSystem instance that data backend by asset
+func AssetFile() http.FileSystem {
+	return &assetOperator{}
+}
+
+`)
+	return err
+}

+ 473 - 0
internal/release.go

@@ -0,0 +1,473 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package bindata
+
+import (
+	"bytes"
+	"compress/gzip"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"unicode/utf8"
+)
+
+// writeRelease writes the release code file.
+func writeRelease(w io.Writer, c *Config, toc []Asset) error {
+	err := writeReleaseHeader(w, c)
+	if err != nil {
+		return err
+	}
+
+	err = writeAssetFS(w, c)
+	if err != nil {
+		return err
+	}
+
+	for i := range toc {
+		err = writeReleaseAsset(w, c, &toc[i])
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// writeReleaseHeader writes output file headers.
+// This targets release builds.
+func writeReleaseHeader(w io.Writer, c *Config) error {
+	var err error
+	if c.NoCompress {
+		if c.NoMemCopy {
+			err = header_uncompressed_nomemcopy(w, c)
+		} else {
+			err = header_uncompressed_memcopy(w, c)
+		}
+	} else {
+		if c.NoMemCopy {
+			err = header_compressed_nomemcopy(w, c)
+		} else {
+			err = header_compressed_memcopy(w, c)
+		}
+	}
+	if err != nil {
+		return err
+	}
+	return header_release_common(w)
+}
+
+// writeReleaseAsset write a release entry for the given asset.
+// A release entry is a function which embeds and returns
+// the file's byte content.
+func writeReleaseAsset(w io.Writer, c *Config, asset *Asset) error {
+	fd, err := os.Open(asset.Path)
+	if err != nil {
+		return err
+	}
+
+	defer fd.Close()
+
+	if c.NoCompress {
+		if c.NoMemCopy {
+			err = uncompressed_nomemcopy(w, asset, fd)
+		} else {
+			err = uncompressed_memcopy(w, asset, fd)
+		}
+	} else {
+		if c.NoMemCopy {
+			err = compressed_nomemcopy(w, asset, fd)
+		} else {
+			err = compressed_memcopy(w, asset, fd)
+		}
+	}
+	if err != nil {
+		return err
+	}
+	return asset_release_common(w, c, asset)
+}
+
+// sanitize prepares a valid UTF-8 string as a raw string constant.
+// Based on https://code.google.com/p/go/source/browse/godoc/static/makestatic.go?repo=tools
+func sanitize(b []byte) []byte {
+	// Replace ` with `+"`"+`
+	b = bytes.Replace(b, []byte("`"), []byte("`+\"`\"+`"), -1)
+
+	// Replace BOM with `+"\xEF\xBB\xBF"+`
+	// (A BOM is valid UTF-8 but not permitted in Go source files.
+	// I wouldn't bother handling this, but for some insane reason
+	// jquery.js has a BOM somewhere in the middle.)
+	return bytes.Replace(b, []byte("\xEF\xBB\xBF"), []byte("`+\"\\xEF\\xBB\\xBF\"+`"), -1)
+}
+
+func header_compressed_nomemcopy(w io.Writer, c *Config) error {
+	var header string
+
+	if c.HttpFileSystem {
+		header = `import (
+	"bytes"
+	"compress/gzip"
+	"fmt"
+	"net/http"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"`
+	} else {
+		header = `import (
+	"bytes"
+	"compress/gzip"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"`
+	}
+
+	_, err := fmt.Fprintf(w, `%s
+)
+
+func bindataRead(data, name string) ([]byte, error) {
+	gz, err := gzip.NewReader(strings.NewReader(data))
+	if err != nil {
+		return nil, fmt.Errorf("read %%q: %%v", name, err)
+	}
+
+	var buf bytes.Buffer
+	_, err = io.Copy(&buf, gz)
+	clErr := gz.Close()
+
+	if err != nil {
+		return nil, fmt.Errorf("read %%q: %%v", name, err)
+	}
+	if clErr != nil {
+		return nil, err
+	}
+
+	return buf.Bytes(), nil
+}
+
+`, header)
+	return err
+}
+
+func header_compressed_memcopy(w io.Writer, c *Config) error {
+	var header string
+
+	if c.HttpFileSystem {
+		header = `import (
+	"bytes"
+	"compress/gzip"
+	"fmt"
+	"net/http"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"`
+	} else {
+		header = `import (
+	"bytes"
+	"compress/gzip"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"`
+	}
+
+	_, err := fmt.Fprintf(w, `%s
+)
+
+func bindataRead(data []byte, name string) ([]byte, error) {
+	gz, err := gzip.NewReader(bytes.NewBuffer(data))
+	if err != nil {
+		return nil, fmt.Errorf("read %%q: %%v", name, err)
+	}
+
+	var buf bytes.Buffer
+	_, err = io.Copy(&buf, gz)
+	clErr := gz.Close()
+
+	if err != nil {
+		return nil, fmt.Errorf("read %%q: %%v", name, err)
+	}
+	if clErr != nil {
+		return nil, err
+	}
+
+	return buf.Bytes(), nil
+}
+
+`, header)
+	return err
+}
+
+func header_uncompressed_nomemcopy(w io.Writer, c *Config) error {
+	var header string
+
+	if c.HttpFileSystem {
+		header = `import (
+	"bytes"
+	"fmt"
+	"net/http"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"strings"
+	"time"
+	"unsafe"`
+	} else {
+		header = `import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"strings"
+	"time"
+	"unsafe"`
+	}
+
+	_, err := fmt.Fprintf(w, `%s
+)
+
+func bindataRead(data, name string) ([]byte, error) {
+	var empty [0]byte
+	sx := (*reflect.StringHeader)(unsafe.Pointer(&data))
+	b := empty[:]
+	bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+	bx.Data = sx.Data
+	bx.Len = len(data)
+	bx.Cap = bx.Len
+	return b, nil
+}
+
+`, header)
+	return err
+}
+
+func header_uncompressed_memcopy(w io.Writer, c *Config) error {
+	var header string
+
+	if c.HttpFileSystem {
+		header = `import (
+	"bytes"
+	"fmt"
+	"net/http"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"`
+	} else {
+		header = `import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"`
+	}
+
+	_, err := fmt.Fprintf(w, `%s
+)
+`, header)
+	return err
+}
+
+func header_release_common(w io.Writer) error {
+	_, err := fmt.Fprintf(w, `type asset struct {
+	bytes []byte
+	info  os.FileInfo
+}
+
+type bindataFileInfo struct {
+	name    string
+	size    int64
+	mode    os.FileMode
+	modTime time.Time
+}
+
+// Name return file name
+func (fi bindataFileInfo) Name() string {
+	return fi.name
+}
+
+// Size return file size
+func (fi bindataFileInfo) Size() int64 {
+	return fi.size
+}
+
+// Mode return file mode
+func (fi bindataFileInfo) Mode() os.FileMode {
+	return fi.mode
+}
+
+// ModTime return file modify time
+func (fi bindataFileInfo) ModTime() time.Time {
+	return fi.modTime
+}
+
+// IsDir return file whether a directory
+func (fi bindataFileInfo) IsDir() bool {
+	return fi.mode&os.ModeDir != 0
+}
+
+// Sys return file is sys mode
+func (fi bindataFileInfo) Sys() interface{} {
+	return nil
+}
+
+`)
+	return err
+}
+
+func compressed_nomemcopy(w io.Writer, asset *Asset, r io.Reader) error {
+	_, err := fmt.Fprintf(w, `var _%s = "`, asset.Func)
+	if err != nil {
+		return err
+	}
+
+	gz := gzip.NewWriter(&StringWriter{Writer: w})
+	_, err = io.Copy(gz, r)
+	gz.Close()
+
+	if err != nil {
+		return err
+	}
+
+	_, err = fmt.Fprintf(w, `"
+
+func %sBytes() ([]byte, error) {
+	return bindataRead(
+		_%s,
+		%q,
+	)
+}
+
+`, asset.Func, asset.Func, asset.Name)
+	return err
+}
+
+func compressed_memcopy(w io.Writer, asset *Asset, r io.Reader) error {
+	_, err := fmt.Fprintf(w, `var _%s = []byte("`, asset.Func)
+	if err != nil {
+		return err
+	}
+
+	gz := gzip.NewWriter(&StringWriter{Writer: w})
+	_, err = io.Copy(gz, r)
+	gz.Close()
+
+	if err != nil {
+		return err
+	}
+
+	_, err = fmt.Fprintf(w, `")
+
+func %sBytes() ([]byte, error) {
+	return bindataRead(
+		_%s,
+		%q,
+	)
+}
+
+`, asset.Func, asset.Func, asset.Name)
+	return err
+}
+
+func uncompressed_nomemcopy(w io.Writer, asset *Asset, r io.Reader) error {
+	_, err := fmt.Fprintf(w, `var _%s = "`, asset.Func)
+	if err != nil {
+		return err
+	}
+
+	_, err = io.Copy(&StringWriter{Writer: w}, r)
+	if err != nil {
+		return err
+	}
+
+	_, err = fmt.Fprintf(w, `"
+
+func %sBytes() ([]byte, error) {
+	return bindataRead(
+		_%s,
+		%q,
+	)
+}
+
+`, asset.Func, asset.Func, asset.Name)
+	return err
+}
+
+func uncompressed_memcopy(w io.Writer, asset *Asset, r io.Reader) error {
+	_, err := fmt.Fprintf(w, `var _%s = []byte(`, asset.Func)
+	if err != nil {
+		return err
+	}
+
+	b, err := ioutil.ReadAll(r)
+	if err != nil {
+		return err
+	}
+	if utf8.Valid(b) && !bytes.Contains(b, []byte{0}) {
+		fmt.Fprintf(w, "`%s`", sanitize(b))
+	} else {
+		fmt.Fprintf(w, "%+q", b)
+	}
+
+	_, err = fmt.Fprintf(w, `)
+
+func %sBytes() ([]byte, error) {
+	return _%s, nil
+}
+
+`, asset.Func, asset.Func)
+	return err
+}
+
+func asset_release_common(w io.Writer, c *Config, asset *Asset) error {
+	fi, err := os.Stat(asset.Path)
+	if err != nil {
+		return err
+	}
+
+	mode := uint(fi.Mode())
+	modTime := fi.ModTime().Unix()
+	size := fi.Size()
+	if c.NoMetadata {
+		mode = 0
+		modTime = 0
+		size = 0
+	}
+	if c.Mode > 0 {
+		mode = uint(os.ModePerm) & c.Mode
+	}
+	if c.ModTime > 0 {
+		modTime = c.ModTime
+	}
+	_, err = fmt.Fprintf(w, `func %s() (*asset, error) {
+	bytes, err := %sBytes()
+	if err != nil {
+		return nil, err
+	}
+
+	info := bindataFileInfo{name: %q, size: %d, mode: os.FileMode(%d), modTime: time.Unix(%d, 0)}
+	a := &asset{bytes: bytes, info: info}
+	return a, nil
+}
+
+`, asset.Func, asset.Func, asset.Name, size, mode, modTime)
+	return err
+}

+ 62 - 0
internal/restore.go

@@ -0,0 +1,62 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package bindata
+
+import (
+	"fmt"
+	"io"
+)
+
+func writeRestore(w io.Writer) error {
+	_, err := fmt.Fprintf(w, `
+// RestoreAsset restores an asset under the given directory
+func RestoreAsset(dir, name string) error {
+	data, err := Asset(name)
+	if err != nil {
+		return err
+	}
+	info, err := AssetInfo(name)
+	if err != nil {
+		return err
+	}
+	err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
+	if err != nil {
+		return err
+	}
+	err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
+	if err != nil {
+		return err
+	}
+	err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// RestoreAssets restores an asset under the given directory recursively
+func RestoreAssets(dir, name string) error {
+	children, err := AssetDir(name)
+	// File
+	if err != nil {
+		return RestoreAsset(dir, name)
+	}
+	// Dir
+	for _, child := range children {
+		err = RestoreAssets(dir, filepath.Join(name, child))
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func _filePath(dir, name string) string {
+	canonicalName := strings.Replace(name, "\\", "/", -1)
+	return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
+}
+`)
+	return err
+}

+ 36 - 0
internal/stringwriter.go

@@ -0,0 +1,36 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package bindata
+
+import (
+	"io"
+)
+
+const lowerHex = "0123456789abcdef"
+
+type StringWriter struct {
+	io.Writer
+	c int
+}
+
+func (w *StringWriter) Write(p []byte) (n int, err error) {
+	if len(p) == 0 {
+		return
+	}
+
+	buf := []byte(`\x00`)
+	var b byte
+
+	for n, b = range p {
+		buf[2] = lowerHex[b/16]
+		buf[3] = lowerHex[b%16]
+		w.Writer.Write(buf)
+		w.c++
+	}
+
+	n++
+
+	return
+}

+ 288 - 0
internal/toc.go

@@ -0,0 +1,288 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package bindata
+
+import (
+	"fmt"
+	"io"
+	"sort"
+	"strings"
+)
+
+type assetTree struct {
+	Asset    Asset
+	Children map[string]*assetTree
+}
+
+func newAssetTree() *assetTree {
+	tree := &assetTree{}
+	tree.Children = make(map[string]*assetTree)
+	return tree
+}
+
+func (node *assetTree) child(name string) *assetTree {
+	rv, ok := node.Children[name]
+	if !ok {
+		rv = newAssetTree()
+		node.Children[name] = rv
+	}
+	return rv
+}
+
+func (root *assetTree) Add(route []string, asset Asset) {
+	for _, name := range route {
+		root = root.child(name)
+	}
+	root.Asset = asset
+}
+
+func ident(w io.Writer, n int) {
+	for i := 0; i < n; i++ {
+		w.Write([]byte{'\t'})
+	}
+}
+
+func (root *assetTree) funcOrNil() string {
+	if root.Asset.Func == "" {
+		return "nil"
+	} else {
+		return root.Asset.Func
+	}
+}
+
+func getFillerSize(tokenIndex int, lengths []int, nident int) int {
+	var (
+		curlen    int = lengths[tokenIndex]
+		maxlen    int = 0
+		substart  int = 0
+		subend    int = 0
+		spacediff int = 0
+	)
+
+	if curlen > 0 {
+		substart = tokenIndex
+		for (substart-1) >= 0 && lengths[substart-1] > 0 {
+			substart -= 1
+		}
+
+		subend = tokenIndex
+		for (subend+1) < len(lengths) && lengths[subend+1] > 0 {
+			subend += 1
+		}
+
+		var candidate int
+		for j := substart; j <= subend; j += 1 {
+			candidate = lengths[j]
+			if candidate > maxlen {
+				maxlen = candidate
+			}
+		}
+
+		spacediff = maxlen - curlen
+	}
+
+	return spacediff
+}
+
+func (root *assetTree) writeGoMap(w io.Writer, nident int) {
+	fmt.Fprintf(w, "&bintree{%s, map[string]*bintree{", root.funcOrNil())
+
+	if len(root.Children) > 0 {
+		io.WriteString(w, "\n")
+
+		// Sort to make output stable between invocations
+		filenames := make([]string, len(root.Children))
+		hasChildren := make(map[string]bool)
+		i := 0
+		for filename, node := range root.Children {
+			filenames[i] = filename
+			hasChildren[filename] = len(node.Children) > 0
+			i++
+		}
+		sort.Strings(filenames)
+
+		lengths := make([]int, len(root.Children))
+		for i, filename := range filenames {
+			if hasChildren[filename] {
+				lengths[i] = 0
+			} else {
+				lengths[i] = len(filename)
+			}
+		}
+
+		for i, p := range filenames {
+			ident(w, nident+1)
+			filler := strings.Repeat(" ", getFillerSize(i, lengths, nident))
+			fmt.Fprintf(w, `"%s": %s`, p, filler)
+			root.Children[p].writeGoMap(w, nident+1)
+		}
+		ident(w, nident)
+	}
+
+	io.WriteString(w, "}}")
+	if nident > 0 {
+		io.WriteString(w, ",")
+	}
+	io.WriteString(w, "\n")
+}
+
+func (root *assetTree) WriteAsGoMap(w io.Writer) error {
+	_, err := fmt.Fprint(w, `type bintree struct {
+	Func     func() (*asset, error)
+	Children map[string]*bintree
+}
+
+var _bintree = `)
+	root.writeGoMap(w, 0)
+	return err
+}
+
+func writeTOCTree(w io.Writer, toc []Asset) error {
+	_, err := fmt.Fprintf(w, `// AssetDir returns the file names below a certain
+// directory embedded in the file by go-bindata.
+// For example if you run go-bindata on data/... and data contains the
+// following hierarchy:
+//     data/
+//       foo.txt
+//       img/
+//         a.png
+//         b.png
+// then AssetDir("data") would return []string{"foo.txt", "img"}
+// AssetDir("data/img") would return []string{"a.png", "b.png"}
+// AssetDir("foo.txt") and AssetDir("nonexistent") would return an error
+// AssetDir("") will return []string{"data"}.
+func AssetDir(name string) ([]string, error) {
+	node := _bintree
+	if len(name) != 0 {
+		canonicalName := strings.Replace(name, "\\", "/", -1)
+		pathList := strings.Split(canonicalName, "/")
+		for _, p := range pathList {
+			node = node.Children[p]
+			if node == nil {
+				return nil, fmt.Errorf("Asset %%s not found", name)
+			}
+		}
+	}
+	if node.Func != nil {
+		return nil, fmt.Errorf("Asset %%s not found", name)
+	}
+	rv := make([]string, 0, len(node.Children))
+	for childName := range node.Children {
+		rv = append(rv, childName)
+	}
+	return rv, nil
+}
+
+`)
+	if err != nil {
+		return err
+	}
+	tree := newAssetTree()
+	for i := range toc {
+		pathList := strings.Split(toc[i].Name, "/")
+		tree.Add(pathList, toc[i])
+	}
+	return tree.WriteAsGoMap(w)
+}
+
+// writeTOC writes the table of contents file.
+func writeTOC(w io.Writer, toc []Asset) error {
+	err := writeTOCHeader(w)
+	if err != nil {
+		return err
+	}
+
+	var maxlen = 0
+	for i := range toc {
+		l := len(toc[i].Name)
+		if l > maxlen {
+			maxlen = l
+		}
+	}
+
+	for i := range toc {
+		err = writeTOCAsset(w, &toc[i], maxlen)
+		if err != nil {
+			return err
+		}
+	}
+
+	return writeTOCFooter(w)
+}
+
+// writeTOCHeader writes the table of contents file header.
+func writeTOCHeader(w io.Writer) error {
+	_, err := fmt.Fprintf(w, `// Asset loads and returns the asset for the given name.
+// It returns an error if the asset could not be found or
+// could not be loaded.
+func Asset(name string) ([]byte, error) {
+	canonicalName := strings.Replace(name, "\\", "/", -1)
+	if f, ok := _bindata[canonicalName]; ok {
+		a, err := f()
+		if err != nil {
+			return nil, fmt.Errorf("Asset %%s can't read by error: %%v", name, err)
+		}
+		return a.bytes, nil
+	}
+	return nil, fmt.Errorf("Asset %%s not found", name)
+}
+
+// MustAsset is like Asset but panics when Asset would return an error.
+// It simplifies safe initialization of global variables.
+func MustAsset(name string) []byte {
+	a, err := Asset(name)
+	if err != nil {
+		panic("asset: Asset(" + name + "): " + err.Error())
+	}
+
+	return a
+}
+
+// AssetInfo loads and returns the asset info for the given name.
+// It returns an error if the asset could not be found or
+// could not be loaded.
+func AssetInfo(name string) (os.FileInfo, error) {
+	canonicalName := strings.Replace(name, "\\", "/", -1)
+	if f, ok := _bindata[canonicalName]; ok {
+		a, err := f()
+		if err != nil {
+			return nil, fmt.Errorf("AssetInfo %%s can't read by error: %%v", name, err)
+		}
+		return a.info, nil
+	}
+	return nil, fmt.Errorf("AssetInfo %%s not found", name)
+}
+
+// AssetNames returns the names of the assets.
+func AssetNames() []string {
+	names := make([]string, 0, len(_bindata))
+	for name := range _bindata {
+		names = append(names, name)
+	}
+	return names
+}
+
+// _bindata is a table, holding each asset generator, mapped to its name.
+var _bindata = map[string]func() (*asset, error){
+`)
+	return err
+}
+
+// writeTOCAsset write a TOC entry for the given asset.
+func writeTOCAsset(w io.Writer, asset *Asset, maxlen int) error {
+	spacediff := maxlen - len(asset.Name)
+	filler := strings.Repeat(" ", spacediff)
+
+	_, err := fmt.Fprintf(w, "\t%q: %s%s,\n", asset.Name, filler, asset.Func)
+	return err
+}
+
+// writeTOCFooter writes the table of contents file footer.
+func writeTOCFooter(w io.Writer) error {
+	_, err := fmt.Fprintf(w, `}
+
+`)
+	return err
+}

+ 8 - 0
internal/tools.go

@@ -0,0 +1,8 @@
+// +build tools
+
+package tools
+
+import (
+	_ "github.com/kisielk/errcheck"
+	_ "golang.org/x/lint/golint"
+)

+ 31 - 0
internal/version.go

@@ -0,0 +1,31 @@
+// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
+// license. Its contents can be found at:
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+package bindata
+
+import (
+	"fmt"
+	"runtime"
+)
+
+const (
+	AppName         = "go-bindata"
+	AppVersionMajor = 3
+	AppVersionMinor = 1
+)
+
+// revision part of the program version.
+// This will be set automatically at build time like so:
+//
+//     go build -ldflags "-X main.AppVersionRev `date -u +%s`"
+var AppVersionRev string
+
+func Version() string {
+	if len(AppVersionRev) == 0 {
+		AppVersionRev = "3"
+	}
+
+	return fmt.Sprintf("%s %d.%d.%s (Go runtime %s).\nCopyright (c) 2010-2013, Jim Teeuwen.",
+		AppName, AppVersionMajor, AppVersionMinor, AppVersionRev, runtime.Version())
+}

+ 87 - 0
logger/logger.go

@@ -0,0 +1,87 @@
+package logger
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"os"
+	"strings"
+)
+
+const (
+	exit = -1
+)
+
+var (
+	ExitError = errors.New("exit error")
+)
+
+type Logger interface {
+	Debugf(format string, v ...interface{})
+	Infof(format string, v ...interface{})
+	Errorf(format string, v ...interface{})
+	Warnf(format string, v ...interface{})
+	Fatalf(format string, v ...interface{})
+}
+
+type stdOutLogger struct {
+	*log.Logger
+}
+
+func NewStdOutLogger(prefix string) Logger {
+	if prefix != "" && !strings.HasSuffix(prefix, " ") {
+		prefix = prefix + " "
+	}
+	return &stdOutLogger{
+		Logger: log.New(os.Stdout, prefix, log.Lshortfile|log.LstdFlags),
+	}
+}
+
+func (s *stdOutLogger) output(level level, str string) {
+	formatStr := ""
+	switch level {
+	case levelFatal:
+		formatStr = "\033[35m[FATAL]\033[0m " + str
+	case levelError:
+		formatStr = "\033[31m[ERROR]\033[0m " + str
+	case levelWarning:
+		formatStr = "\033[33m[WARN]\033[0m " + str
+	case levelInfo:
+		formatStr = "\033[32m[INFO]\033[0m " + str
+	case levelDebug:
+		formatStr = "\033[36m[DEBUG]\033[0m " + str
+	}
+	_ = s.Output(3, formatStr)
+}
+
+func (s *stdOutLogger) Debugf(format string, v ...interface{}) {
+	s.output(levelDebug, fmt.Sprintf(format, v...))
+}
+
+func (s *stdOutLogger) Infof(format string, v ...interface{}) {
+	s.output(levelInfo, fmt.Sprintf(format, v...))
+}
+func (s *stdOutLogger) Warnf(format string, v ...interface{}) {
+	s.output(levelWarning, fmt.Sprintf(format, v...))
+}
+func (s *stdOutLogger) Errorf(format string, v ...interface{}) {
+	s.output(levelError, fmt.Sprintf(format, v...))
+}
+
+func (s *stdOutLogger) Fatalf(format string, v ...interface{}) {
+	s.output(levelError, fmt.Sprintf(format, v...))
+	//os.Exit(exit)
+	panic(ExitError)
+}
+
+type (
+	level int
+)
+
+const (
+	levelFatal level = iota + 1
+	levelError
+	levelWarning
+	levelInfo
+	levelDebug
+)

+ 38 - 0
logger/std_logger.go

@@ -0,0 +1,38 @@
+package logger
+
+import (
+	"fmt"
+	"os"
+	"runtime"
+)
+
+var (
+	GeneratorName = "GEN"
+)
+
+// \033[33m[WARN]\033[0m
+// \033[36m[DEBUG]\033[0m
+
+func InfoF(format string, v ...interface{}) {
+	str := fmt.Sprintf("\033[32m[%s-INFO]\033[0m %s\n", GeneratorName, format)
+	fmt.Printf(str, v...)
+}
+
+func ErrorF(format string, v ...interface{}) {
+	_, file, line, _ := runtime.Caller(1)
+	str := fmt.Sprintf("\033[31m[%s-ERROR]\033[0m %s.%d %s\n", GeneratorName, file, line, format)
+	fmt.Printf(str, v...)
+}
+
+func FatalF(format string, v ...interface{}) {
+	str := fmt.Sprintf("\033[35m[%s-FATAL]\033[0m %s\n", GeneratorName, format)
+	fmt.Printf(str, v...)
+	os.Exit(-1)
+}
+
+func FatalFH(format string, fun func()) {
+	str := fmt.Sprintf("\033[35m[%s-FATAL]\033[0m %s\n", GeneratorName, format)
+	fmt.Print(str)
+	fun()
+	os.Exit(-1)
+}

+ 95 - 0
orm/core.go

@@ -0,0 +1,95 @@
+package orm
+
+import (
+	"fmt"
+	"gitea.ckfah.com/go-script/logger"
+	"github.com/go-xorm/xorm"
+	"os"
+	"strings"
+	"sync"
+	"text/template"
+)
+
+type DbType string
+
+// type
+const (
+	Mysql = DbType("mysql")
+)
+
+// template name
+const (
+	daoTemplateName   = "daoTemplateName"
+	dtoTemplateName   = "dtoTemplateName"
+	modelTemplateName = "modelTemplateName"
+)
+
+// other
+const (
+	sep        = string(os.PathSeparator)
+	fileSuffix = ".go"
+)
+
+type Template interface {
+	Run(template *template.Template) ([]byte, error)
+}
+
+type DbMeta interface {
+	GetTables() ([]string, error)
+}
+
+type Config struct {
+	wg            sync.WaitGroup
+	daoTemplate   *template.Template
+	modelTemplate *template.Template
+	dtoTemplate   *template.Template
+
+	// db
+	DbType     DbType
+	DbName     string
+	DbPort     int
+	DbHost     string
+	DbUserName string
+	DbPassword string
+	DbCharset  string
+
+	// cnn
+	engine *xorm.Engine
+
+	// save
+	SaveFile string
+
+	// table
+	TableNames []string
+
+	// template
+	GeneratorModel   bool
+	GeneratorDao     bool
+	GeneratorDto     bool
+	DaoPackageName   string
+	ModelPackageName string
+	DtoPackageName   string
+	Tags             []string
+}
+
+func FillDNS(userName string, password string, host string, port int, dbName string, charset string) string {
+	return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
+		userName,
+		password,
+		host,
+		port,
+		dbName,
+		charset,
+	)
+}
+
+func GetDbType(str string) DbType {
+	lower := strings.ToLower(str)
+	switch DbType(lower) {
+	case Mysql:
+		return Mysql
+	default:
+		logger.FatalF("not support type")
+		return Mysql
+	}
+}

+ 79 - 0
orm/dao.go

@@ -0,0 +1,79 @@
+package orm
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"gitea.ckfah.com/go-script/logger"
+	"gitea.ckfah.com/go-script/utils"
+	"go/format"
+	"text/template"
+)
+
+const (
+	defaultModelName = "model"
+	defaultDaoName   = "dao"
+)
+
+type DaoMeta struct {
+	DaoPackage   string
+	TableName    string
+	ModelPackage string
+}
+
+func (this *DaoMeta) Run(temp *template.Template) ([]byte, error) {
+	var buf = &bytes.Buffer{}
+	err := this.Validate()
+	if err != nil {
+		return nil, err
+	}
+	err = temp.Execute(buf, this)
+	if err != nil {
+		return nil, err
+	}
+	source, err := format.Source(buf.Bytes())
+	if err != nil {
+		logger.ErrorF("[Run] err,table=%v,%v,body=\n%s\n", this.TableName, err, buf.Bytes())
+		return buf.Bytes(), nil
+	}
+	return source, nil
+}
+
+/**
+获取 struct name
+*/
+func (this *DaoMeta) GetStructName() string {
+	return utils.LowerCaseFiledFirst(fmt.Sprintf("%sDao", this.GetModelName()))
+}
+
+/**
+获取new初始化的名称
+*/
+func (this *DaoMeta) GetNewStructFunc() string {
+	return fmt.Sprintf("New%sDao", this.GetModelName())
+}
+
+func (this *DaoMeta) GetModelName() string {
+	return utils.Marshal(this.TableName)
+}
+
+func (this *DaoMeta) GetModelPackage() string {
+	return this.ModelPackage
+}
+
+func (this *DaoMeta) GetModelPathName() string {
+	return fmt.Sprintf("%s.%s", this.GetModelPackage(), this.GetModelName())
+}
+
+func (this *DaoMeta) Validate() error {
+	if this.TableName == "" {
+		return errors.New("table_name is nil")
+	}
+	if this.ModelPackage == "" {
+		this.ModelPackage = defaultModelName
+	}
+	if this.DaoPackage == "" {
+		this.DaoPackage = defaultDaoName
+	}
+	return nil
+}

+ 40 - 0
orm/db.go

@@ -0,0 +1,40 @@
+package orm
+
+import (
+	"github.com/go-xorm/xorm"
+)
+
+const (
+	tableNameKey = "table_name"
+)
+
+const (
+	tableNamesSql = `select table_name from information_schema.tables where table_schema = ? and table_type = 'base table';`
+)
+
+type mysqlMeta struct {
+	Db     *xorm.Engine
+	DbName string
+}
+
+func NewMysqlMeta(dbName string, db *xorm.Engine) DbMeta {
+	return &mysqlMeta{
+		Db:     db,
+		DbName: dbName,
+	}
+}
+
+func (this *mysqlMeta) GetTables() ([]string, error) {
+	list, err := this.Db.SQL(tableNamesSql, this.DbName).QueryString()
+	if err != nil {
+		return nil, err
+	}
+	result := make([]string, 0, len(list))
+	for _, e := range list {
+		tableName, isExist := e[tableNameKey]
+		if isExist {
+			result = append(result, tableName)
+		}
+	}
+	return result, nil
+}

+ 65 - 0
orm/dto.go

@@ -0,0 +1,65 @@
+package orm
+
+import (
+	"bytes"
+	"fmt"
+	"gitea.ckfah.com/go-script/logger"
+	"go/format"
+	"sync"
+	"text/template"
+)
+
+const (
+	defaultDtoPackageName = "dto"
+)
+
+type DtoMeta struct {
+	TableName  string
+	TableField []FieldMeta
+}
+
+type DtoMetas struct {
+	sync.Mutex
+	packageName string
+	dtos        []DtoMeta
+}
+
+func NewDtoMeta(packageName string) *DtoMetas {
+	if packageName == "" {
+		packageName = defaultDtoPackageName
+	}
+	return &DtoMetas{
+		packageName: packageName,
+		dtos:        []DtoMeta{},
+	}
+}
+
+func (this *DtoMetas) Append(meta *ModelMeta) {
+	this.Lock()
+	defer this.Unlock()
+	dtoMeta := DtoMeta{
+		TableName:  meta.TableName,
+		TableField: meta.Fields,
+	}
+	this.dtos = append(this.dtos, dtoMeta)
+}
+
+func (this *DtoMetas) Run(tmpl *template.Template) ([]byte, error) {
+	var buffer = &bytes.Buffer{}
+	buffer.WriteString(fmt.Sprintf(`
+	package %s
+
+	`, this.packageName))
+	for _, elem := range this.dtos {
+		err := tmpl.Execute(buffer, &elem)
+		if err != nil {
+			return nil, err
+		}
+	}
+	source, err := format.Source(buffer.Bytes())
+	if err != nil {
+		logger.ErrorF("[Run] err,table=%v,err=%v,body=\n%s\n", this.packageName, err, buffer.Bytes())
+		return buffer.Bytes(), nil
+	}
+	return source, nil
+}

+ 194 - 0
orm/gennerator.go

@@ -0,0 +1,194 @@
+package orm
+
+import (
+	"fmt"
+	"gitea.ckfah.com/go-script/file"
+	"gitea.ckfah.com/go-script/logger"
+	"gitea.ckfah.com/go-script/orm/temp"
+	"gitea.ckfah.com/go-script/utils"
+	"github.com/go-xorm/xorm"
+	"path/filepath"
+	"text/template"
+)
+
+func (config *Config) initTemplate() error {
+	{
+		parse, err := template.New(modelTemplateName).Parse(temp.Model)
+		if err != nil {
+			return err
+		}
+		config.modelTemplate = parse
+	}
+	{
+		parse, err := template.New(daoTemplateName).Parse(temp.Dao)
+		if err != nil {
+			return err
+		}
+		config.daoTemplate = parse
+	}
+	{
+		parse, err := template.New(dtoTemplateName).Funcs(map[string]interface{}{
+			"Upper": utils.Marshal,
+		}).Parse(temp.Dto)
+		if err != nil {
+			return err
+		}
+		config.dtoTemplate = parse
+	}
+
+	return nil
+}
+
+func (config *Config) initDb() error {
+	engine, err := xorm.NewEngine(string(config.DbType), FillDNS(config.DbUserName, config.DbPassword, config.DbHost, config.DbPort, config.DbName, config.DbCharset))
+	if err != nil {
+		return err
+	}
+	err = engine.Ping()
+	if err != nil {
+		return err
+	}
+	config.engine = engine
+	if config.TableNames == nil || len(config.TableNames) == 0 {
+		tableNames, err := NewMysqlMeta(config.DbName, engine).GetTables()
+		if err != nil {
+			return err
+		}
+		config.TableNames = tableNames
+	}
+	return nil
+}
+
+func (config *Config) Generator() error {
+	dir, err := filepath.Abs(config.SaveFile)
+	if err != nil {
+		return err
+	}
+	config.SaveFile = dir
+	logger.InfoF("save path: %s", dir)
+	config.initPackage()
+
+	err = config.initDb()
+	if err != nil {
+		return err
+	}
+	err = config.initTemplate()
+	if err != nil {
+		return err
+	}
+	var dtoMetas *DtoMetas
+	if config.GeneratorDto {
+		dtoMetas = NewDtoMeta(config.DtoPackageName)
+	}
+	for _, tableName := range config.TableNames {
+		config.wg.Add(1)
+		go func(config *Config, tableName string, dtoMetas *DtoMetas) {
+			defer func() {
+				if err := recover(); err != nil {
+					logger.FatalF("generate err: %v", err)
+				}
+				config.wg.Done()
+			}()
+			switch config.DbType {
+			case Mysql:
+				func() {
+					err := mysql(config, tableName, dtoMetas)
+					if err != nil {
+						logger.FatalF("generate err: %v", err)
+					}
+				}()
+			default:
+				logger.FatalF("not support %s db type", config.DbType)
+			}
+		}(config, tableName, dtoMetas)
+	}
+	config.wg.Wait()
+	return nil
+}
+
+func (config *Config) initPackage() {
+	if config.ModelPackageName == "" {
+		config.ModelPackageName = defaultModelName
+	}
+	if config.DaoPackageName == "" {
+		config.DaoPackageName = defaultDaoName
+	}
+	if config.DtoPackageName == "" {
+		config.DtoPackageName = defaultDtoPackageName
+	}
+}
+
+func mysql(config *Config, tableName string, dtoMetas *DtoMetas) error {
+	if config.GeneratorModel {
+		model := NewModelMeta(config.DbName, tableName, config.ModelPackageName, config.engine, config.Tags)
+		modelBody, err := model.Run(config.modelTemplate)
+		if err != nil {
+			return err
+		}
+		saveFilePath := getSaveFileName(config.SaveFile, config.ModelPackageName, tableName)
+		err = file.WriteFile(saveFilePath, modelBody)
+		if err != nil {
+			return err
+		}
+		logger.InfoF("save %s model success, path=%s", tableName, saveFilePath)
+		if config.GeneratorDto && dtoMetas != nil {
+			dtoMetas.Append(model)
+		}
+	}
+	if config.GeneratorDao {
+		dao := DaoMeta{
+			TableName:    tableName,
+			DaoPackage:   config.DaoPackageName,
+			ModelPackage: config.ModelPackageName,
+		}
+		daoBody, err := dao.Run(config.daoTemplate)
+		if err != nil {
+			return err
+		}
+		saveFilePath := getSaveFileName(config.SaveFile, config.DaoPackageName, getDaoFileName(tableName))
+		err = file.WriteFile(saveFilePath, daoBody)
+		if err != nil {
+			return err
+		}
+		logger.InfoF("save %s dao success, path= %s", tableName, saveFilePath)
+	}
+	if config.GeneratorDto {
+		err := config.saveDtoFile(tableName, dtoMetas)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+/**
+dto file 坚持一个文件一个db
+ */
+func (config *Config) saveDtoFile(tableName string, dtoMetas *DtoMetas) error {
+	if (!config.GeneratorDto) || dtoMetas == nil {
+		return nil
+	}
+	dtoFile := getSaveFileName(config.SaveFile, config.DtoPackageName, getDtoFileName(tableName))
+	bytes, err := dtoMetas.Run(config.dtoTemplate)
+	if err != nil {
+		return err
+	}
+	err = file.WriteFile(dtoFile, bytes)
+	if err != nil {
+		return err
+	}
+	logger.InfoF("save %s dto success, path= %s", config.DbName, dtoFile)
+	return nil
+}
+
+func getDaoFileName(tableName string) string {
+	return fmt.Sprintf("%s_dao", tableName)
+}
+
+func getDtoFileName(dbName string) string {
+	return fmt.Sprintf("%s_dto", dbName)
+}
+
+func getSaveFileName(dir, packageName, fileName string) string {
+	return fmt.Sprintf("%s%s%s%s%s%s", dir, sep, packageName, sep, fileName, fileSuffix)
+}

+ 230 - 0
orm/model.go

@@ -0,0 +1,230 @@
+package orm
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"gitea.ckfah.com/go-script/logger"
+	"gitea.ckfah.com/go-script/utils"
+	_ "github.com/go-sql-driver/mysql"
+	"github.com/go-xorm/xorm"
+	"go/format"
+	"strings"
+	"text/template"
+)
+
+const (
+	CreateTable = "Create Table"
+)
+
+const (
+	specifiedTableNamesSql = `select table_name from information_schema.tables where table_schema = ? and table_name in ('%s') and table_type = 'base table';`
+	tableColumnsSql        = `select column_name as column_name,column_comment as column_comment,
+is_nullable as is_nullable, if(column_type = 'tinyint(1)', 'boolean', data_type) as file_type
+from information_schema.columns
+where table_schema = ? and  table_name = ?
+order by ordinal_position;
+`
+)
+
+type FieldMeta struct {
+	Name          string
+	FieldType     string
+	IsNullable    string //OK  YES
+	ColumnComment string // 字段描述信息
+	Tags          []string
+}
+
+func (this *FieldMeta) GetDtoTag() string {
+	return fmt.Sprintf("`json:\"%s\"`", this.Name)
+}
+
+func (this *FieldMeta) GetTag() string {
+	var (
+		tags = make([]string, 0)
+	)
+	for _, elem := range this.Tags {
+		if strings.Compare(elem, "xorm") == 0 {
+			if strings.Compare(this.Name, "id") == 0 {
+				tags = append(tags, fmt.Sprintf("xorm:\"%s\"", "pk autoincr id"))
+				continue
+			}
+			if strings.Compare(this.Name, "create_time") == 0 || strings.Compare(this.Name, "ctime") == 0 {
+				tags = append(tags, fmt.Sprintf("xorm:\"%s %s\"", "created", this.Name))
+				continue
+			}
+			if strings.Compare(this.Name, "update_time") == 0 || strings.Compare(this.Name, "utime") == 0 {
+				tags = append(tags, fmt.Sprintf("xorm:\"%s %s\"", "updated", this.Name))
+				continue
+			}
+		}
+		tags = append(tags, fmt.Sprintf("%s:\"%s\"", elem, this.Name))
+	}
+	if len(tags) == 0 {
+		return ""
+	}
+	var tagStr = ""
+	for _, elem := range tags {
+		tagStr += fmt.Sprintf("%s ", elem)
+	}
+	if len(tagStr) > 0 {
+		tagStr = strings.TrimRight(tagStr, " ")
+	}
+	return fmt.Sprintf("`%s`", tagStr)
+}
+
+func (this *FieldMeta) GetGoField() string {
+	return utils.Marshal(this.Name)
+}
+
+func (this *FieldMeta) GetGoDaoType() string {
+	switch this.FieldType {
+	case "bit", "tinyint", "boolean":
+		return "uint8"
+	case "smallint", "year":
+		return "uint16"
+	case "integer", "mediumint", "int":
+		return "int"
+	case "bigint":
+		return "uint64"
+	case "date", "timestamp without time zone", "timestamp with time zone", "time with time zone", "time without time zone",
+		"timestamp", "datetime", "time":
+		return "int64" // 时间全部是int64
+	case "byte",
+		"binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob":
+		return "[]byte"
+	case "text", "character", "character varying", "tsvector", "bit varying", "money", "json", "jsonb", "xml", "point", "interval", "line", "ARRAY",
+		"char", "varchar", "tinytext", "mediumtext", "longtext":
+		return "string"
+	case "real":
+		return "float32"
+	case "numeric", "decimal", "double precision", "float", "double":
+		return "float64"
+	default:
+		return "string"
+	}
+}
+
+func (this *FieldMeta) GetGoType() string {
+	switch this.FieldType {
+	case "bit", "tinyint", "boolean":
+		return "uint8"
+	case "smallint", "year":
+		return "uint16"
+	case "integer", "mediumint", "int":
+		return "int"
+	case "bigint":
+		return "uint64"
+	case "date", "timestamp without time zone", "timestamp with time zone", "time with time zone", "time without time zone",
+		"timestamp", "datetime", "time":
+		return "time.Time"
+	case "byte",
+		"binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob":
+		return "[]byte"
+	case "text", "character", "character varying", "tsvector", "bit varying", "money", "json", "jsonb", "xml", "point", "interval", "line", "ARRAY",
+		"char", "varchar", "tinytext", "mediumtext", "longtext":
+		return "string"
+	case "real":
+		return "float32"
+	case "numeric", "decimal", "double precision", "float", "double":
+		return "float64"
+	default:
+		return "string"
+	}
+}
+
+type ModelMeta struct {
+	DbName          string
+	TableName       string
+	PackageName     string
+	Fields          []FieldMeta
+	Db              *xorm.Engine
+	Tags            []string
+	hasGetFieldInfo bool
+}
+
+func NewModelMeta(dbName string, tableName string, packageName string, db *xorm.Engine, tags []string) *ModelMeta {
+	return &ModelMeta{
+		DbName:      dbName,
+		TableName:   tableName,
+		PackageName: packageName,
+		Db:          db,
+		Tags:        tags,
+	}
+}
+
+func (this *ModelMeta) ModelName() string {
+	return utils.Marshal(this.TableName)
+}
+
+func (this *ModelMeta) Run(template *template.Template) ([]byte, error) {
+	if this.Db == nil {
+		return nil, errors.New("the db is nil")
+	}
+	err := this.FindField()
+	if err != nil {
+		return nil, err
+	}
+	var body = &bytes.Buffer{}
+	err = template.Execute(body, this)
+	if err != nil {
+		return nil, err
+	}
+	source, err := format.Source(body.Bytes())
+	if err != nil {
+		logger.ErrorF("[Run] err,table=%v,err=%v,body=\n%s\n", this.TableName, err, body.Bytes())
+		return body.Bytes(), nil
+	}
+	return source, nil
+
+}
+
+func (this *ModelMeta) FindField() error {
+	if this.hasGetFieldInfo {
+		return nil
+	}
+	queryString, err := this.Db.SQL(tableColumnsSql, this.DbName, this.TableName).QueryString()
+	if err != nil {
+		return err
+	}
+	metas := make([]FieldMeta, 0, len(queryString))
+	for _, elem := range queryString {
+		metas = append(metas, FieldMeta{
+			Name:          elem["column_name"],
+			FieldType:     elem["file_type"],
+			IsNullable:    elem["is_nullable"],
+			ColumnComment: elem["column_comment"],
+			Tags:          this.Tags,
+		})
+	}
+	this.Fields = metas
+	this.hasGetFieldInfo = true
+	return nil
+}
+
+func (this *ModelMeta) GetCreateSql() (string, error) {
+	result, err := this.Db.SQL(fmt.Sprintf("SHOW CREATE TABLE %s", this.TableName)).QueryString()
+	if err != nil {
+		return "", err
+	}
+	var sql = ""
+	for _, e := range result {
+		str, isExist := e[CreateTable]
+		if isExist {
+			sql = str
+		}
+	}
+	if sql == "" {
+		return "", errors.New(fmt.Sprintf("can not fond %s create table sql", this.TableName))
+	}
+	return sql, nil
+}
+
+func (this *ModelMeta) HasTimeFiled() bool {
+	for _, e := range this.Fields {
+		if strings.Compare(e.GetGoType(), "time.Time") == 0 {
+			return true
+		}
+	}
+	return false
+}

+ 101 - 0
orm/temp/dao.go.tmpl

@@ -0,0 +1,101 @@
+{{$model_mame:=.GetModelPathName}}
+{{$dao_mame:=.GetStructName}}
+
+package {{.DaoPackage}}
+
+import (
+	"context"
+)
+
+type {{$dao_mame}} struct {
+}
+
+func {{.GetNewStructFunc}}() *{{$dao_mame}} {
+	return &{{$dao_mame}}{}
+}
+
+var (
+	_{{.GetModelName}} = new({{$model_mame}})
+)
+
+func (*{{$dao_mame}}) TableName() string {
+	return _{{.GetModelName}}.TableName()
+}
+
+func (this *{{$dao_mame}}) GetById(ctx context.Context, id uint64) (*{{$model_mame}}, cerror.Cerror) {
+	result := &{{$model_mame}}{}
+	isExist, err := db.SlaveDb().Where("id=?", id).Cols("*").Get(result)
+	if err != nil {
+		logger.Errorc(ctx, "[GetById] err,id=%d", id)
+		return nil, exception.DbExecError(err)
+	}
+	if !isExist {
+		return nil, nil
+	}
+	return result, nil
+}
+
+func (this *{{$dao_mame}}) GetByIds(ctx context.Context, ids []uint64) ([]{{$model_mame}}, cerror.Cerror) {
+	list := make([]{{$model_mame}}, 0)
+	err := db.SlaveDb().In("id", ids).Cols("*").Find(&list)
+	if err != nil {
+		logger.Errorc(ctx, "[GetByIds] err,ids=%v", ids)
+		return nil, exception.DbExecError(err)
+	}
+	return list, nil
+}
+
+func (this *{{$dao_mame}}) DeleteById(ctx context.Context, session *xorm.Session, id uint64) (int64, cerror.Cerror) {
+	effectRow, err := session.Where("id=?", id).Delete(this)
+	if err != nil {
+		logger.Errorc(ctx, "[DeleteById] err,id=%d", id)
+		return 0, exception.DbDeleteError(err)
+	}
+	return effectRow, nil
+}
+
+func (this *{{$dao_mame}}) UpdateById(ctx context.Context, session *xorm.Session, id uint64, params map[string]interface{}) (int64, cerror.Cerror) {
+	effectRow, err := session.Table(this).Where("id=?", id).Update(params)
+	if err != nil {
+		logger.Errorc(ctx, "[UpdateById] err,id=%d,params=%+v", id, params)
+		return 0, exception.DbUpdateError(err)
+	}
+	return effectRow, nil
+}
+
+func (this *{{$dao_mame}}) UpdateByIds(ctx context.Context, session *xorm.Session, ids []uint64, params map[string]interface{}) (int64, cerror.Cerror) {
+	effectRow, err := session.Table(this).In("id", ids).Update(params)
+	if err != nil {
+		logger.Errorc(ctx, "[UpdateByIds] err,ids=%d,params=%+v", ids, params)
+		return 0, exception.DbUpdateError(err)
+	}
+	return effectRow, nil
+}
+
+func (this *{{$dao_mame}}) SaveOne(ctx context.Context, session *xorm.Session, param *{{$model_mame}}) (int64, cerror.Cerror) {
+	effectRow, err := session.InsertOne(param)
+	if err != nil {
+		logger.Errorc(ctx, "[SaveOne] err,param=%+v", param)
+		return 0, exception.DbInsertError(err)
+	}
+	return effectRow, nil
+}
+
+func (this *{{$dao_mame}}) SaveMany(ctx context.Context, session *xorm.Session, params *[]{{$model_mame}}) (int64, cerror.Cerror) {
+	effectRow, err := session.Insert(params)
+	if err != nil {
+		logger.Errorc(ctx, "[SaveMany] err,params=%+v", params)
+		return 0, exception.DbInsertError(err)
+	}
+	return effectRow, nil
+}
+
+func (this *{{$dao_mame}}) GetAll(ctx context.Context) ([]{{$model_mame}}, cerror.Cerror) {
+	list := make([]{{$model_mame}}, 0)
+	err := db.SlaveDb().Cols("*").Find(&list)
+	if err != nil {
+		logger.Errorc(ctx, "[GetAll] err")
+		return nil, exception.DbExecError(err)
+	}
+	return list, nil
+}

+ 6 - 0
orm/temp/dto.go.tmpl

@@ -0,0 +1,6 @@
+package dto
+
+type {{Upper .TableName}}Dto struct {
+    {{range .TableField}}
+	{{.GetGoField}} {{.GetGoDaoType}} {{.GetDtoTag}} {{if .ColumnComment}}//{{.ColumnComment}}{{end}}{{end}}
+}

+ 20 - 0
orm/temp/model.go.tmpl

@@ -0,0 +1,20 @@
+package {{.PackageName}}
+
+{{if .HasTimeFiled}}
+import (
+	"time"
+)
+{{end}}
+
+/**
+{{.GetCreateSql}}
+*/
+
+type {{.ModelName}} struct {
+    {{range .Fields}}
+    {{.GetGoField}} {{.GetGoType}} {{if .GetTag}}{{.GetTag}}{{end}}{{end}}
+}
+
+func (*{{.ModelName}}) TableName() string {
+	return "{{.TableName}}"
+}

+ 233 - 0
orm/temp/tmpl.go

@@ -0,0 +1,233 @@
+package temp
+
+var Dao = `
+{{$modelName:=.GetModelPathName}}
+{{$daoName:=.GetStructName}}
+
+package {{.DaoPackage}}
+
+import (
+	"cjjy-cases-server/business/dto"
+	"cjjy-cases-server/business/exception"
+	"cjjy-cases-server/business/model"
+	"cjjy-cases-server/business/util/pagination"
+	"context"
+	"gitea.ckfah.com/cjjy/gocommon/pkg/cerror"
+	"gitea.ckfah.com/cjjy/gocommon/pkg/database/db"
+	"gitea.ckfah.com/cjjy/gocommon/pkg/logger"
+	"xorm.io/xorm"
+)
+
+type {{$daoName}} struct {
+}
+
+func {{.GetNewStructFunc}}() *{{$daoName}} {
+	return &{{$daoName}}{}
+}
+
+var (
+	_{{.GetModelName}} = new({{$modelName}})
+)
+
+func (*{{$daoName}}) TableName() string {
+	return _{{.GetModelName}}.TableName()
+}
+
+func (thisDao *{{$daoName}}) GetById(ctx context.Context, id uint64) (*{{$modelName}}, cerror.Cerror) {
+	methodName := "{{$daoName}}.GetById"
+	logger.Infoc(ctx, "[%s] start,id:%d", methodName, id)
+	result := &{{$modelName}}{}
+	session := db.StdSlaveDB().Where("id=?", id)
+	isExist, err := SoftDeleteFilterDao(ctx, session).Cols("*").Get(result)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call db.Get() err,err:%+v,id:%d", methodName, err, id)
+		return nil, exception.DbExecError(err)
+	}
+	logger.Infoc(ctx, "[%s] end,id:%d,result:%+v", methodName, id, result)
+	if !isExist {
+		return nil, nil
+	}
+	return result, nil
+}
+
+func (thisDao *{{$daoName}}) GetByIds(ctx context.Context, ids []uint64) ([]{{$modelName}}, cerror.Cerror) {
+	methodName := "{{$daoName}}.GetByIds"
+	logger.Infoc(ctx, "[%s] start,ids:%+v", methodName, ids)
+	result := make([]{{$modelName}}, 0)
+	session := db.StdSlaveDB().In("id", ids)
+	err := SoftDeleteFilterDao(ctx, session).Cols("*").Find(&result)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call db.Find() err,err:%+v,ids:%+v", methodName, err, ids)
+		return nil, exception.DbExecError(err)
+	}
+	logger.Infoc(ctx, "[%s] end,ids:%+v,result:%+v", methodName, ids, result)
+	return result, nil
+}
+
+func (thisDao *{{$daoName}}) GetMapsByIds(ctx context.Context, ids []uint64) (map[uint64]{{$modelName}}, cerror.Cerror) {
+	methodName := "{{$daoName}}.GetMapsByIds"
+	logger.Infoc(ctx, "[%s] start,ids:%+v", methodName, ids)
+	resultMaps := make(map[uint64]{{$modelName}})
+	result, err := thisDao.GetByIds(ctx, ids)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call thisDao.GetByIds() err,err:%+v,ids:%+v", methodName, err, ids)
+		return nil, exception.DbExecError(err)
+	}
+	if len(result) > 0 {
+		for _, item := range result {
+			resultMaps[item.ID] = item
+		}
+	}
+	logger.Infoc(ctx, "[%s] end,ids:%+v,result:%+v,resultMaps:%+v", methodName, ids, result, resultMaps)
+	return resultMaps, nil
+}
+
+func (thisDao *{{$daoName}}) DeleteById(ctx context.Context, session *xorm.Session, id uint64) (int64, cerror.Cerror) {
+	methodName := "{{$daoName}}.DeleteById"
+	logger.Infoc(ctx, "[%s] start,id:%d", methodName, id)	
+	effectRow, err := SoftDeleteFilterDao(ctx, session).Where("id=?", id).Delete(thisDao)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call db.Delete() err,err:%+v,id:%d", methodName, err, id)
+		return 0, exception.DbDeleteError(err)
+	}
+	logger.Infoc(ctx, "[%s] end,id:%d,effectRow:%+v", methodName, id, effectRow)
+	return effectRow, nil
+}
+
+func (thisDao *{{$daoName}}) UpdateById(ctx context.Context, session *xorm.Session, params *model.CjjyCase) (int64, cerror.Cerror) {
+	methodName := "{{$daoName}}.UpdateById"
+	logger.Infoc(ctx, "[%s] start,params:%+v", methodName, params)
+	effectRow, err := session.Table(thisDao).Where("id=?", params.ID).Update(params)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call db.Update() err,err:%+v,params:%+v", methodName, err, params)
+		return 0, exception.DbUpdateError(err)
+	}
+	logger.Infoc(ctx, "[%s] end,params:%+v,effectRow:%d", methodName, params, effectRow)
+	return effectRow, nil
+}
+
+func (thisDao *{{$daoName}}) UpdateMapById(ctx context.Context, session *xorm.Session, id uint64, params map[string]interface{}) (int64, cerror.Cerror) {
+	methodName := "{{$daoName}}.UpdateMapById"
+	logger.Infoc(ctx, "[%s] start,id:%d,params:%+v", methodName, id, params)
+	effectRow, err := SoftDeleteFilterDao(ctx, session).Table(thisDao).Where("id=?", id).Update(params)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call db.Update() err,err:%+v,id:%d,params:%+v", methodName, err, id, params)
+		return 0, exception.DbUpdateError(err)
+	}
+	logger.Infoc(ctx, "[%s] end,id:%d,params:%+v,effectRow:%+v", methodName, id, params, effectRow)
+	return effectRow, nil
+}
+
+func (thisDao *{{$daoName}}) UpdateMapByIds(ctx context.Context, session *xorm.Session, ids []uint64, params map[string]interface{}) (int64, cerror.Cerror) {
+	methodName := "{{$daoName}}.UpdateMapByIds"	
+	logger.Infoc(ctx, "[%s] start,ids:%+v,params:%+v", methodName, ids, params)	
+	effectRow, err := SoftDeleteFilterDao(ctx, session).Table(thisDao).In("id", ids).Update(params)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call db.Update() err,err:%+v,ids:%+v,params:%+v", methodName, err, ids, params)
+		return 0, exception.DbUpdateError(err)
+	}
+	logger.Infoc(ctx, "[%s] end,ids:%+v,params:%+v,effectRow:%+v", methodName, ids, params, effectRow)
+	return effectRow, nil
+}
+
+func (thisDao *{{$daoName}}) InsertOne(ctx context.Context, session *xorm.Session, params *{{$modelName}}) (int64, cerror.Cerror) {
+	methodName := "{{$daoName}}.InsertOne"
+	logger.Infoc(ctx, "[%s] start,params:%+v", methodName, params)
+	effectRow, err := session.InsertOne(params)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call db.InsertOne() err,err:%+v,params:%+v", methodName, err, params)
+		return 0, exception.DbInsertError(err)
+	}
+	logger.Infoc(ctx, "[%s] end,params:%+v,effectRow:%+v", methodName, params, effectRow)
+	return effectRow, nil
+}
+
+func (thisDao *{{$daoName}}) InsertMany(ctx context.Context, session *xorm.Session, params *[]{{$modelName}}) (int64, cerror.Cerror) {
+	methodName := "{{$daoName}}.InsertMany"
+	logger.Infoc(ctx, "[%s] start,params:%+v", methodName, params)
+	effectRow, err := session.Insert(params)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call db.Insert() err,err:%+v,params:%+v", methodName, err, params)
+		return 0, exception.DbInsertError(err)
+	}
+	logger.Infoc(ctx, "[%s] end,params:%+v,effectRow:%+v", methodName, params, effectRow)
+	return effectRow, nil
+}
+
+func (thisDao *{{$daoName}}) GetAll(ctx context.Context) ([]{{$modelName}}, cerror.Cerror) {
+	methodName := "{{$daoName}}.GetAll"
+	logger.Infoc(ctx, "[%s] start", methodName)
+	result := make([]{{$modelName}}, 0)
+	session := db.StdSlaveDB().Cols("*")
+	err := SoftDeleteFilterDao(ctx, session).Find(&result)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call db.Find() err,err:%+v", methodName, err)
+		return nil, exception.DbExecError(err)
+	}
+	logger.Infoc(ctx, "[%s] end,result:%+v", methodName, result)
+	return result, nil
+}
+
+func (thisDao *{{$daoName}}) GetAllMaps(ctx context.Context) (map[uint64]{{$modelName}}, cerror.Cerror) {
+	methodName := "{{$daoName}}.GetAllMaps"
+	logger.Infoc(ctx, "[%s] start", methodName)
+	resultMaps := make(map[uint64]{{$modelName}})
+	result, err := thisDao.GetAll(ctx)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call thisDao.GetAll() err,err:%+v", methodName, err)
+		return nil, exception.DbExecError(err)
+	}
+	if len(result) > 0 {
+		for _, item := range result {
+			resultMaps[item.ID] = item
+		}
+	}
+	logger.Infoc(ctx, "[%s] end,result:%+v,resultMaps:%+v", methodName, result, resultMaps)
+	return resultMaps, nil
+}
+
+func (thisDao *{{$daoName}}) GetPageList(ctx context.Context, params *dto.GetPageListParams) ([]{{$modelName}}, int64, cerror.Cerror) {
+	methodName := "{{$daoName}}.GetPageList"	
+	logger.Infoc(ctx, "[%s] start,params:%+v", methodName, params)
+	result := make([]{{$modelName}}, 0)
+	session := db.StdSlaveDB().Table(new({{$modelName}}))
+	start, limit := pagination.GetLimitAndCount(params.NewPageRequest)
+	count, err := SoftDeleteFilterDao(ctx, session).Limit(limit, start).Desc("id").FindAndCount(&result)
+	if err != nil {
+		logger.Errorc(ctx, "[%s] call db.FindAndCount() err,err:%+v,params:%+v", methodName, err, params)
+		return nil, 0, exception.DbExecError(err)
+	}
+	logger.Infoc(ctx, "[%s] end,result:%+v,count:%d,params:%+v", methodName, result, count, params)
+	return result, count, nil
+}
+`
+
+var Model = `
+package {{.PackageName}}
+
+{{if .HasTimeFiled}}
+import (
+	"time"
+)
+{{end}}
+
+/**
+{{.GetCreateSql}}
+*/
+
+type {{.ModelName}} struct {
+    {{range .Fields}}
+    {{.GetGoField}} {{.GetGoType}} {{if .GetTag}}{{.GetTag}}{{end}}{{end}}
+}
+
+func (*{{.ModelName}}) TableName() string {
+	return "{{.TableName}}"
+}
+`
+
+var Dto = `
+type {{Upper .TableName}}Dto struct {
+    {{range .TableField}}
+	{{.GetGoField}} {{.GetGoDaoType}} {{.GetDtoTag}} {{if .ColumnComment}}//{{.ColumnComment}}{{end}}{{end}}
+}
+`

+ 53 - 0
scrpit/cmd.go

@@ -0,0 +1,53 @@
+package scrpit
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+)
+
+// func main() {
+//	exec.Command("git", "clone", "git@gitee.com:Anthony-Dong/template.git")
+//}
+func Git(str, dir string) {
+	gitCmd := fmt.Sprintf("git clone %s  %s", str, dir)
+	Cmd(gitCmd)
+}
+
+func GitBranch(branch, str, dir string) {
+	gitCmd := fmt.Sprintf("git clone -b %s %s  %s", branch, str, dir)
+	Cmd(gitCmd)
+}
+
+func Run(shell string) {
+	gitCmd := fmt.Sprintf("%s", shell)
+	Cmd(gitCmd)
+}
+
+func Copy(src, dest string) {
+	gitCmd := fmt.Sprintf("cp %s %s", src, dest)
+	Cmd(gitCmd)
+}
+
+func Mv(src, dest string) {
+	gitCmd := fmt.Sprintf("mv %s %s", src, dest)
+	Cmd(gitCmd)
+}
+
+// delete file
+func Delete(file string) {
+	gitCmd := fmt.Sprintf("rm -rf %s", file)
+	Cmd(gitCmd)
+}
+
+func Cmd(cmd string) {
+	fmt.Println("/bin/bash " + "-c " + cmd)
+	command := exec.Command("/bin/bash", "-c", cmd)
+	command.Stdout = os.Stdout
+	command.Stderr = os.Stdout
+	err := command.Run()
+	if err != nil {
+		log.Fatal(fmt.Sprintf("run %s err", "/bin/bash "+"-c "+cmd))
+	}
+}

+ 116 - 0
shell/code.shell

@@ -0,0 +1,116 @@
+#!/bin/sh
+
+has_errors=0
+
+# 获取git暂存的所有go代码
+# --cached 暂存的
+# --name-only 只显示名字
+# --diff-filter=ACM 过滤暂存文件,A=Added C=Copied M=Modified, 即筛选出添加/复制/修改的文件
+allgofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
+
+echo "\033[32m------------------------代码开始检测-------------------------------\033[0m\n"
+
+gofiles=()
+godirs=()
+for allfile in ${allgofiles[@]}; do 
+    # 过滤vendor的
+    # 过滤prootobuf自动生产的文件
+	# 过滤测试文件
+    if [[ $allfile == "vendor"* || $allfile == *".pb.go" || $allfile == *"_test.go" ]];then
+        continue
+    else
+        gofiles+=("$allfile")
+
+        # 文件夹去重
+        existdir=0
+        dir=`echo "$allfile" |xargs -n1 dirname|sort -u`
+        for somedir in ${godirs[@]}; do
+            if [[ $dir == $somedir ]]; then 
+                existdir=1
+                break
+            fi
+        done
+
+        if [[ $existdir -eq 0 ]]; then 
+            godirs+=("$dir")
+        fi
+    fi
+done
+
+[ -z "$gofiles" ] && exit 0
+
+# gofmt 格式化代码
+unformatted=$(/data/go/bin/gofmt -l ${gofiles[@]})
+if [ -n "$unformatted" ]; then
+	echo >&2 "\033[31m gofmt 工具发现代码格式不标准,请运行以下命令,然后再git add 提交代码: \033[0m"
+	echo "-------------------------------------------------------------------"
+	for f in ${unformatted[@]}; do
+		echo >&2 "gofmt -d -w $PWD/$f"
+		# gofmt -d -w $PWD/$f
+	done
+	echo "-------------------------------------------------------------------\n\n"
+	has_errors=1
+fi
+
+# goimports 自动格式化,包工具
+if /data/go/bin/goimports >/dev/null 2>&1; then  # 检测是否安装
+	unimports=$(/data/go/bin/goimports -l ${gofiles[@]})
+	if [ -n "$unimports" ]; then
+		echo >&2 "\033[31m goimports 工具发现import格式不标准,请运行以下命令,然后再git add 提交代码: \033[0m"
+		echo "-------------------------------------------------------------------"
+		for f in ${unimports[@]} ; do
+			echo >&2 "goimports -d -w $PWD/$f"
+		done
+		echo "-------------------------------------------------------------------\n\n"
+		has_errors=1
+	fi
+else
+	echo 'Error: goimports not install. Run: "go get -u golang.org/x/tools/cmd/goimports"' >&2
+	exit 1
+fi
+
+# golint 代码规范检测(目前不使用)
+# echo >&2 "\033[32m golint 工具检测代码是否标准,如果发现有不标准的,请检测后再提交代码(不要求强制优化):\033[0m\n"
+# echo "-------------------------------------------------------------------"
+# if /data/go/bin/golint >/dev/null 2>&1; then  # 检测是否安装
+# 	lint_errors=false
+# 	for file in ${gofiles[@]} ; do
+# 		lint_result="$(/data/go/bin/golint $file)" # run golint
+# 		if test -n "$lint_result" ; then
+# 			echo "golint '$file':\n$lint_result"
+# 			lint_errors=true
+# 		fi
+# 	done
+# 	# if [ $lint_errors = true ] ; then
+# 	# 	echo ""
+# 	# fi
+# else
+# 	echo 'Error: golint 工具未安装,请执行命令: "go get -u github.com/golang/lint/golint"' >&2
+# 	exit 1
+# fi
+# echo "-------------------------------------------------------------------\n"
+
+# go vet 静态错误检查,检测代码是否能编译
+# show_vet_header=true
+
+# for dir in ${godirs[@]} ; do
+#     vet=$(go vet $PWD/$dir 2>&1)
+#     if [ -n "$vet" -a $show_vet_header = true ] ; then
+# 	echo "\033[31m govet 检测代码存在异常,请仔细检查以下代码,修改完成后,然后再git add 提交代码: \033[0m"
+# 	show_vet_header=false
+#     fi
+# 	echo "-------------------------------------------------------------------\n"
+#     if [ -n "$vet" ] ; then
+# 	echo "$vet\n"
+# 	has_errors=1
+#     fi
+# 	echo "-------------------------------------------------------------------\n\n"
+# done
+
+if [ $has_errors -eq 1 ]; then
+echo "\033[31m------------代码检测失败,发现异常,请及时修复后再提交-------------\033[0m\n"
+else 
+echo "\033[32m----------代码检测成功,未发现异常,可以 commit 代码---------------\033[0m\n"
+fi;
+
+exit $has_errors

+ 11 - 0
shell/message.shell

@@ -0,0 +1,11 @@
+#!/bin/sh
+
+MSG=`awk '{printf("%s",$0)}' $1`
+if [ ${#MSG} -lt 10 ]  
+  then
+    echo "\n\033[31m------------------Git commit message 发现异常----------------------\033[0m"
+    echo "commit message 只有${#MSG}字符"
+    echo "message的长度不能小于10, 本次提交失败,请完善commit message,再提交"
+    echo "\033[31m—------------------------------------------------------------------\033[0m\n"
+    exit 1
+fi

+ 111 - 0
shell/new-githook.shell

@@ -0,0 +1,111 @@
+#!/bin/sh
+
+
+if [ ! -e /data/go/bin ];then
+	echo "/data/go/bin  目录不存在, 正在创建"
+	echo "mkdir -p /data/go/bin"
+	mkdir -p /data/go/bin
+	echo "chmod 777 -R /data/go/bin"
+	chmod -R  777 /data/go/bin
+fi
+
+
+CURDIR=`pwd`
+echo "当前目录:${CURDIR},开始执行创建git hook 脚本"
+
+GitPath="${CURDIR}/.git"
+if [ ! -d $GitPath ]; then
+	echo "请执行 git init 初始化git 项目"
+	exit -1
+fi
+
+GitHookPath="${CURDIR}/.git/hooks"
+if [ ! -d $GitHookPath ]; then
+	echo "$GitHookPath 不存在"
+	exit -1
+fi
+
+
+# 下载脚本
+curl -O https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/code.shell
+curl -O https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/message.shell
+
+# copy 文件,如果存在,则备份历史文本
+# commit go 文件检测,脚本
+GitCommitCheckPath="${GitPath}/hooks/pre-commit"
+GitCommitCheckOldPath="$GitCommitCheckPath.old"
+
+if [ -f $GitCommitCheckPath ]; then
+ echo "cp $GitCommitCheckPath $GitCommitCheckOldPath"
+ cp $GitCommitCheckPath $GitCommitCheckOldPath
+fi;
+
+echo "mv code.shell $GitCommitCheckPath"
+mv code.shell $GitCommitCheckPath
+chmod 755 $GitCommitCheckPath
+
+
+#  commit 消息脚本
+GitCommitMessagePath="${GitPath}/hooks/commit-msg"
+GitCommitMessageOldPath="$GitCommitMessagePath.old"
+if [ -f $GitCommitMessagePath ]; then
+ echo "cp $GitCommitMessagePath $GitCommitMessageOldPath"
+ cp $GitCommitMessagePath $GitCommitMessageOldPath
+fi;
+echo "mv message.shell $GitCommitMessagePath"
+mv message.shell $GitCommitMessagePath
+echo "chmod 755 $GitCommitMessagePath"
+chmod 755 $GitCommitMessagePath
+
+GoFmtPath=/data/go/bin/gofmt
+
+if [ ! -f $GoFmtPath ];then 
+	echo "curl -o $GoFmtPath https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/gofmt"
+	curl -o $GoFmtPath https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/gofmt
+	echo "chmod 777 $GoFmtPath"
+	chmod 777 $GoFmtPath
+fi
+
+
+GoImportPath=/data/go/bin/goimports
+
+if [ ! -f $GoImportPath ];then 
+	echo "curl -o $GoImportPath https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/goimports"
+	curl -o $GoImportPath https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/goimports
+	echo "chmod 777 $GoImportPath"
+	chmod 777 $GoImportPath
+fi
+
+GoLinePath=/data/go/bin/golint
+
+if [ ! -f $GoLinePath ];then 
+	echo "curl -o $GoLinePath https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/golint"
+	curl -o $GoLinePath https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/golint
+	echo "chmod 777 $GoLinePath"
+	chmod 777 $GoLinePath
+fi
+
+
+
+
+
+# https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/gofmt
+
+# https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/goimports
+
+# https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/golint
+
+
+# rm ./.git/hooks/pre-commit
+# rm ./.git/hooks/commit-msg
+
+# curl -O https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/code.shell
+# curl -O https://tyut.oss-cn-beijing.aliyuncs.com/static/shell/message.shell
+
+# mv code.shell ./.git/hooks/pre-commit
+# chmod 755 ./.git/hooks/pre-commit
+
+# mv message.shell ./.git/hooks/commit-msg
+# chmod 755 ./.git/hooks/commit-msg
+
+echo "执行完成,请创建 git hook 脚本成功,请提交代码试一试 .... "

+ 83 - 0
static/file/application.properties

@@ -0,0 +1,83 @@
+# 更多配置文件属性介绍,请看:http://gitlab.corp.cjjyProject:8360/ebike-city-op/gocommon/blob/master/README.md
+
+# nacos dev地址:http://10.100.101.20:8848/nacos/#/configurationManagement?dataId=&group=&appName=&namespace=&pageSize=&pageNo=&serverId=
+# nacos 城市端的用户名密码:cityservice_dev_nacos ,密码:MrHu0qvmdK
+# apollo dev地址:http://apollo.in.cjjyProject:8070/
+# apollo 用户名:员工账号 ,密码:员工密码
+
+
+# application,这里启动的时候记得变更port
+application.project_name=go-template
+application.port=13058
+
+# logger,配置前需要创建/data/log/go-template文件夹
+log.access_log=/data/log/go-template/access.log
+log.monitor_log=/data/log/go-template/monitor.log
+log.task_log=/data/log/go-template/task.log
+log.project_log=/data/log/go-template/go-template.log
+
+# mysql配置
+mysql.host=10.9.198.84
+mysql.port=3306
+mysql.user=ttyc
+mysql.password=ttyongche@2014
+mysql.dbname=urban_violation
+mysql.slave_host=10.9.198.84
+mysql.slave_port=3306
+mysql.slave_user=ttyc
+mysql.slave_password=ttyongche@2014
+mysql.slave_dbname=urban_violation
+mysql.show_sql=true
+mysql.log_file=/data/log/go-template/urban_violation.log
+
+# redis配置
+redis.host=10.9.188.145:6379
+redis.cache_prefix=go-template_dev
+redis.max_conn=100
+redis.max_idle=50
+
+# ebike-factory-api nacos获取ip
+ebike-factory-api.service_name=ebike-factory-api
+ebike-factory-api.group_name=DEFAULT_GROUP
+ebike-factory-api.clusters=DEFAULT
+
+# 本地缓存配置
+localcache.refresh_time=600
+localcache.demo=10
+
+# skywalking配置,这里可以访问web页面:http://10.100.72.97:8080/
+trace.sky_walking_host=10.100.72.97:11800
+trace.application_name=go-template_dev
+
+## city-config,qconf获取ip
+city-config.host=10.9.181.34:20108
+city-config.key=30346876b3f45bab965823b9aa64a1a4
+city-config.name=city-config
+
+## kafka
+# 需要绑定本地的host
+#10.100.101.122 kafka01.dev.in.aaa.com
+
+
+kafka.host=kafka01.dev.in.cjjyProject:9092,kafka02.dev.in.cjjyProject:9092,kafka03.dev.in.cjjyProject:9092
+kafka.log_path=/data/log/go-template/kafka.log
+# 多个Topic可以配置多个
+kafka.topic=business_event,city_op_event
+kafka.group_name=go-template_dev
+
+# kafka-topic business_event
+kafka_consumer_business_event.topic=business_event_dev
+kafka_consumer_business_event.process_num=3
+
+# kafka-topic city_op_event
+kafka_consumer_city_op_event.topic=go-template_dev
+kafka_consumer_city_op_event.process_num=3
+
+## nacos-server 配置
+## 切记本地环境别注册到远程,你可以在本地env设置nacos-server.group_name=local进行配置,其次是目前都注册在public的namespace下,所以这里不需要配置namespace_id
+nacos-server.host=10.100.101.20:8848,10.100.103.230:8848,10.100.99.14:8848
+nacos-server.log_path=/data/log/go-template
+nacos-server.user_name=cityservice_dev_nacos
+nacos-server.password=MrHu0qvmdK
+nacos-server.namespace_id=84663e76-f64a-4331-a3a6-efd26ead7bf1
+nacos-server.log_level=error

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 80 - 0
static/go-static.go


+ 12 - 0
utils/field.go

@@ -0,0 +1,12 @@
+package utils
+
+func LowerCaseFiledFirst(str string) string {
+	if str == "" {
+		return str
+	}
+	arr := []byte(str)
+	if arr[0] >= 'A' && arr[0] <= 'Z' {
+		arr[0] = arr[0] + 32
+	}
+	return string(arr)
+}

+ 20 - 0
utils/reflect.go

@@ -0,0 +1,20 @@
+package utils
+
+import (
+	"reflect"
+)
+
+func ObjToMap(bean interface{}, filter func(fieldName string) string) map[string]interface{} {
+	value := reflect.Indirect(reflect.ValueOf(bean))
+	if value.Kind() != reflect.Struct {
+		panic("the bean mush struct")
+	}
+	_type := value.Type()
+	fieldNum := value.NumField()
+	_map := make(map[string]interface{}, fieldNum)
+	for x := 0; x < fieldNum; x++ {
+		field := _type.Field(x)
+		_map[filter(field.Name)] = value.Field(x).Interface()
+	}
+	return _map
+}

+ 99 - 0
utils/string.go

@@ -0,0 +1,99 @@
+package utils
+
+import (
+	"bytes"
+	"strings"
+)
+
+var commonInitialisms = []string{"ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS"}
+var commonInitialismsReplacer *strings.Replacer
+var uncommonInitialismsReplacer *strings.Replacer
+
+func init() {
+	var commonInitialismsForReplacer []string
+	var uncommonInitialismsForReplacer []string
+	for _, initialism := range commonInitialisms {
+		commonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, strings.Title(strings.ToLower(initialism)))
+		uncommonInitialismsForReplacer = append(uncommonInitialismsForReplacer, strings.Title(strings.ToLower(initialism)), initialism)
+	}
+	commonInitialismsReplacer = strings.NewReplacer(commonInitialismsForReplacer...)
+	uncommonInitialismsReplacer = strings.NewReplacer(uncommonInitialismsForReplacer...)
+}
+
+/**
+下划线 -> 驼峰
+ */
+func Marshal(name string) string {
+	if name == "" {
+		return ""
+	}
+
+	temp := strings.Split(name, "_")
+	var s string
+	for _, v := range temp {
+		vv := []rune(v)
+		if len(vv) > 0 {
+			if bool(vv[0] >= 'a' && vv[0] <= 'z') { //首字母大写
+				vv[0] -= 32
+			}
+			s += string(vv)
+		}
+	}
+
+	s = uncommonInitialismsReplacer.Replace(s)
+	//smap.Set(name, s)
+	return s
+}
+
+/**
+驼峰->下划线
+*/
+func UnMarshal(name string) string {
+	const (
+		lower = false
+		upper = true
+	)
+
+	if name == "" {
+		return ""
+	}
+
+	var (
+		value                                    = commonInitialismsReplacer.Replace(name)
+		buf                                      = bytes.NewBufferString("")
+		lastCase, currCase, nextCase, nextNumber bool
+	)
+
+	for i, v := range value[:len(value)-1] {
+		nextCase = bool(value[i+1] >= 'A' && value[i+1] <= 'Z')
+		nextNumber = bool(value[i+1] >= '0' && value[i+1] <= '9')
+
+		if i > 0 {
+			if currCase == upper {
+				if lastCase == upper && (nextCase == upper || nextNumber == upper) {
+					buf.WriteRune(v)
+				} else {
+					if value[i-1] != '_' && value[i+1] != '_' {
+						buf.WriteRune('_')
+					}
+					buf.WriteRune(v)
+				}
+			} else {
+				buf.WriteRune(v)
+				if i == len(value)-2 && (nextCase == upper && nextNumber == lower) {
+					buf.WriteRune('_')
+				}
+			}
+		} else {
+			currCase = upper
+			buf.WriteRune(v)
+		}
+		lastCase = currCase
+		currCase = nextCase
+	}
+
+	buf.WriteByte(value[len(value)-1])
+
+	s := strings.ToLower(buf.String())
+	return s
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно