diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 9498e50..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.coding_release.yml.sample b/.coding_release.yml.sample index 6a30e49..e2bd646 100644 --- a/.coding_release.yml.sample +++ b/.coding_release.yml.sample @@ -1,10 +1,6 @@ -eid: empty service: - name: e-coding - migrate: - sql: enterprise/app/e-coding/doc/mysql/migrate_script - env-add: enterprise/app/e-coding/doc/mysql/migrate_script - env-remove: enterprise/app/e-coding/doc/mysql/migrate_script + migrate: enterprise/app/e-coding/doc/mysql/migrate_script source: - enterprise/app/e-coding - enterprise/controller/core diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 34b153b..0000000 --- a/Gopkg.lock +++ /dev/null @@ -1,229 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" - name = "github.com/fsnotify/fsnotify" - packages = ["."] - pruneopts = "UT" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - version = "v1.4.7" - -[[projects]] - branch = "master" - digest = "1:1ba1d79f2810270045c328ae5d674321db34e3aae468eb4233883b473c5c0467" - name = "github.com/golang/glog" - packages = ["."] - pruneopts = "UT" - revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" - -[[projects]] - branch = "master" - digest = "1:a361611b8c8c75a1091f00027767f7779b29cb37c456a71b8f2604c88057ab40" - name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/printer", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token", - ] - pruneopts = "UT" - revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168" - -[[projects]] - digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" - name = "github.com/inconshreveable/mousetrap" - packages = ["."] - pruneopts = "UT" - revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" - version = "v1.0" - -[[projects]] - digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7" - name = "github.com/magiconair/properties" - packages = ["."] - pruneopts = "UT" - revision = "c2353362d570a7bfa228149c62842019201cfb71" - version = "v1.8.0" - -[[projects]] - digest = "1:78bbb1ba5b7c3f2ed0ea1eab57bdd3859aec7e177811563edc41198a760b06af" - name = "github.com/mitchellh/go-homedir" - packages = ["."] - pruneopts = "UT" - revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4" - version = "v1.0.0" - -[[projects]] - digest = "1:645110e089152bd0f4a011a2648fbb0e4df5977be73ca605781157ac297f50c4" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - pruneopts = "UT" - revision = "fa473d140ef3c6adf42d6b391fe76707f1f243c8" - version = "v1.0.0" - -[[projects]] - branch = "master" - digest = "1:7aefb397a53fc437c90f0fdb3e1419c751c5a3a165ced52325d5d797edf1aca6" - name = "github.com/moul/http2curl" - packages = ["."] - pruneopts = "UT" - revision = "9ac6cf4d929b2fa8fd2d2e6dec5bb0feb4f4911d" - -[[projects]] - digest = "1:d776f3e95774a8719f2e57fabbbb33103035fe072dcf6f1864f33abd17b753e5" - name = "github.com/parnurzeal/gorequest" - packages = ["."] - pruneopts = "UT" - revision = "a578a48e8d6ca8b01a3b18314c43c6716bb5f5a3" - version = "v0.2.15" - -[[projects]] - digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" - name = "github.com/pelletier/go-toml" - packages = ["."] - pruneopts = "UT" - revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" - version = "v1.2.0" - -[[projects]] - digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "UT" - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" - -[[projects]] - digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84" - name = "github.com/spf13/afero" - packages = [ - ".", - "mem", - ] - pruneopts = "UT" - revision = "787d034dfe70e44075ccc060d346146ef53270ad" - version = "v1.1.1" - -[[projects]] - digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f" - name = "github.com/spf13/cast" - packages = ["."] - pruneopts = "UT" - revision = "8965335b8c7107321228e3e3702cab9832751bac" - version = "v1.2.0" - -[[projects]] - digest = "1:645cabccbb4fa8aab25a956cbcbdf6a6845ca736b2c64e197ca7cbb9d210b939" - name = "github.com/spf13/cobra" - packages = ["."] - pruneopts = "UT" - revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" - version = "v0.0.3" - -[[projects]] - branch = "master" - digest = "1:8a020f916b23ff574845789daee6818daf8d25a4852419aae3f0b12378ba432a" - name = "github.com/spf13/jwalterweatherman" - packages = ["."] - pruneopts = "UT" - revision = "14d3d4c518341bea657dd8a226f5121c0ff8c9f2" - -[[projects]] - digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9" - name = "github.com/spf13/pflag" - packages = ["."] - pruneopts = "UT" - revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" - version = "v1.0.2" - -[[projects]] - digest = "1:4fc8a61287ccfb4286e1ca5ad2ce3b0b301d746053bf44ac38cf34e40ae10372" - name = "github.com/spf13/viper" - packages = ["."] - pruneopts = "UT" - revision = "907c19d40d9a6c9bb55f040ff4ae45271a4754b9" - version = "v1.1.0" - -[[projects]] - branch = "master" - digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8" - name = "golang.org/x/crypto" - packages = ["ssh/terminal"] - pruneopts = "UT" - revision = "614d502a4dac94afa3a6ce146bd1736da82514c6" - -[[projects]] - branch = "master" - digest = "1:a412caf33b5d3c7938c28e1e745c10664bd180277b73ab3b34c0c467886f9969" - name = "golang.org/x/net" - packages = [ - "idna", - "publicsuffix", - ] - pruneopts = "UT" - revision = "faa378e6dbaed88bd8100f8bcf09939375c6e8fa" - -[[projects]] - branch = "master" - digest = "1:7710ea76bd73d3154e86eaf2db8b36a6dffbd6114e4e37b516f1d232c03ddd8d" - name = "golang.org/x/sys" - packages = [ - "unix", - "windows", - ] - pruneopts = "UT" - revision = "11551d06cbcc94edc80a0facaccbda56473c19c1" - -[[projects]] - digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable", - ] - pruneopts = "UT" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "UT" - revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" - version = "v2.2.1" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/golang/glog", - "github.com/mitchellh/go-homedir", - "github.com/parnurzeal/gorequest", - "github.com/spf13/cobra", - "github.com/spf13/pflag", - "github.com/spf13/viper", - "golang.org/x/crypto/ssh/terminal", - "gopkg.in/yaml.v2", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 675abc8..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,35 +0,0 @@ -[[constraint]] - name = "github.com/mitchellh/go-homedir" - version = "1.0.0" - -[[constraint]] - name = "github.com/spf13/cobra" - version = "0.0.3" - -[[constraint]] - name = "github.com/spf13/viper" - version = "1.1.0" - -[[constraint]] - branch = "master" - name = "golang.org/x/crypto" - -[[constraint]] - branch = "master" - name = "golang.org/x/net" - -[prune] - go-tests = true - unused-packages = true - -[[constraint]] - branch = "master" - name = "github.com/golang/glog" - -[[constraint]] - name = "github.com/parnurzeal/gorequest" - version = "0.2.15" - -[[constraint]] - name = "gopkg.in/yaml.v2" - version = "2.2.1" \ No newline at end of file diff --git a/README.md b/README.md index b5b5f53..c9cb5da 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,20 @@ -#Coding Release 发布工具 +# Coding Release 发布工具 ## 简介 用于创建 Coding 发布使用的 Release Checklist 文档 +## 安装 + +windows 用户请先设置环境变量 $GOBIN + +```bash +curl https://raw.githubusercontent.com/coding/coding-cli/master/install.sh | sh +``` + +或者下载源码编译安装 + ## 命令 ### 登录用户 @@ -17,12 +27,13 @@ 在当前目录生成 Markdown 格式的 Release 文件 -示例命令:`coding-cli release master enterprise-saas -o release-20181030.1-enterprise.md -p enterprise-saas -t normal -n 1 -c ~/.coding_release.yml` +示例命令:`coding-cli release release-20181122 general-products -p coding-frontend -o release-20181122-general-products.md` -查看帮助:`coding-cli release --h` +示例命令:`coding-cli release master enterprise-saas -o release-20181030.1-enterprise.md -l enterprise-saas -t normal -n 1 -c ~/.coding_release.yml` -![图片](https://dn-coding-net-production-pp.codehub.cn/f8f39bb8-a3f9-44ba-b6b4-747e8aa2f8d0.png) +查看帮助:`coding-cli release -h` +![图片](https://dn-coding-net-production-pp.codehub.cn/e34c95cb-3ca7-4f2e-b081-03efdce7c036.png) ### 创建环境变量文件 @@ -46,7 +57,6 @@ ### .coding_release.yml 文件示例 ```yml -eid: empty service: - name: e-coding migrate: enterprise/app/e-coding/doc/mysql/migrate_script diff --git a/build.sh b/build.sh deleted file mode 100755 index 76b601b..0000000 --- a/build.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash - -set -eo pipefail - -APP_NAME=coding-cli -MAIN=main.go -ARCH=amd64 -OS=( - darwin - linux - windows -) -DIST_DIR=dist - -function GO_BUILD() { - executable=${APP_NAME}-${GOOS}-${GOARCH} - if [ $GOOS == "windows" ]; then - executable=${APP_NAME}-${GOOS}-${GOARCH}.exe - fi - go build -ldflags="-s -w" -o $DIST_DIR/$executable $MAIN - echo " $executable done." - if hash upx 2>/dev/null; then - echo " Compressing binary size using UPX for $executable" - upx --ultra-brute $DIST_DIR/$executable - echo " Compressed for $executable" - else - echo " No UPX installed, binary would not be comporessed" - if [ `uname` == "Darwin" ]; then - echo " Install UPX using brew: \`brew install upx\`" - fi - fi -} - -# Cleanup dist -rm -rf $DIST_DIR - -# Build exe -echo "Building executables..." -for os in "${OS[@]}"; do - GOOS=$os GOARCH=$ARCH GO_BUILD -done -echo "All executables built." -echo "" - -# Package all exe -echo "Packaging executables and configuration file..." -zip -r $DIST_DIR/coding-cli.zip $DIST_DIR > /dev/null -echo "" - -echo "All done." diff --git a/cmd/env.go b/cmd/env.go deleted file mode 100644 index 4e2509a..0000000 --- a/cmd/env.go +++ /dev/null @@ -1,114 +0,0 @@ -package cmd - -import ( - "bytes" - "html/template" - "io/ioutil" - "strings" - "time" - - "github.com/golang/glog" - "github.com/spf13/cobra" -) - -const ( - featureFlag = "feature" - contentFlag = "content" - fileNameTemplate = `{{.date}}-{{.feature}}.{{.type}}.{{.ext}}` -) - -var content string -var feature string -var append bool - -var evnCmd = &cobra.Command{ - Use: "env", - Short: "生成环境变量文件", - Long: `使用命令行生成环境变量修改的 Checklist 文件`, - Run: func(cmd *cobra.Command, args []string) { - - }, -} - -var addEvnCmd = &cobra.Command{ - Use: "add", - Short: "新增环境变量", - Long: `使用命令行生成环境变量新增的 Checklist 文件 -示例:coding-cli env add -c "redis.host=17.0.0.1" -f add_redis_host -未指定 --output(-o) 将会在当前目录生成文件 YYYY-MM-DD-add_redis_host.add.env 文件,使用 -a=true 可以在指定 --output 文件中添加内容`, - Run: func(cmd *cobra.Command, args []string) { - save(content, "add", "env") - }, -} - -var removeEvnCmd = &cobra.Command{ - Use: "remove", - Short: "移除环境变量", - Long: `使用命令行生成环境变量删除的 Checklist 文件 -示例:coding-cli env remove -c "redis.host=17.0.0.1" -f remove_redis_host -未指定 --output(-o) 将会在当前目录生成文件 YYYY-MM-DD-remove_redis_host.remove.env 文件,使用 -a=true 可以在指定 --output 文件中添加内容`, - Run: func(cmd *cobra.Command, args []string) { - save(content, "remove", "env") - }, -} - -var modifyEvnCmd = &cobra.Command{ - Use: "modify", - Short: "修改环境变量", - Long: `使用命令行生成环境变量修改的 Checklist 文件 -示例:coding-cli env modify -c "redis.host=17.0.0.1" -f modify_redis_host -未指定 --output(-o) 将会在当前目录生成文件 YYYY-MM-DD-modify_redis_host.modify.env 文件,使用 -a=true 可以在指定 --output 文件中添加内容`, - Run: func(cmd *cobra.Command, args []string) { - save(content, "modify", "env") - }, -} - -func save(text string, ftype string, ext string) { - fn := filename(ftype, ext) - if len(output) > 0 { - fn = output - } - if !append { - ioutil.WriteFile(fn, []byte(text), 0666) - } else { - v, err := ioutil.ReadFile(fn) - if err != nil { - glog.Exitln("读取文件失败", fn) - } - ioutil.WriteFile(fn, []byte(strings.Join([]string{string(v), content}, "\n")), 0666) - } -} - -func filename(ftype string, ext string) string { - tpl, err := template.New("filename").Parse(fileNameTemplate) - if err != nil { - glog.Exitln("生成文件名失败") - } - today := time.Now().Format("2006-01-02") - var buf bytes.Buffer - err = tpl.Execute(&buf, map[string]string{ - "date": today, - featureFlag: feature, - "type": ftype, - "ext": ext, - }) - if err != nil { - glog.Exitln("生成文件名失败") - } - return buf.String() -} - -func init() { - evnCmd.AddCommand(addEvnCmd, removeEvnCmd, modifyEvnCmd) - rootCmd.AddCommand(evnCmd) - - for _, subCmd := range []*cobra.Command{addEvnCmd, removeEvnCmd, modifyEvnCmd} { - subCmd.Flags().StringVarP(&content, contentFlag, "c", "", "环境变量名") - subCmd.Flags().StringVarP(&feature, featureFlag, "f", "", "功能名") - subCmd.Flags().StringVarP(&output, "output", "o", "", "输出文件") - subCmd.Flags().BoolVarP(&append, "append", "a", false, "是否覆盖,否则添加到文件中") - - subCmd.MarkFlagRequired(contentFlag) - subCmd.MarkFlagRequired(featureFlag) - } -} diff --git a/cmd/login.go b/cmd/login.go deleted file mode 100644 index 537df3e..0000000 --- a/cmd/login.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright © 2018 彭博 -// -// 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. - -package cmd - -import ( - "fmt" - "os" - "syscall" - - "e.coding.net/codingcorp/coding-cli/pkg/api" - "github.com/spf13/cobra" - "golang.org/x/crypto/ssh/terminal" -) - -const ( - accountFlag = "account" - passwordFlag = "password" - minAccountSize = 6 -) - -var account string -var password string - -// loginCmd represents the login command -var loginCmd = &cobra.Command{ - Use: "login", - Short: "登录到企业版", - Long: `使用 Coding 企业版用户名(邮箱或手机号)和密码登录。`, - Run: func(cmd *cobra.Command, args []string) { - if len(account) >= 3 { - if len(password) <= 0 { - var err error - password, err = readPassword() - if err != nil { - fmt.Fprintf(os.Stderr, "\n读取密码失败,%v\n", err) - return - } - } - api.Login(account, password, readTwoFACode) - return - } - fmt.Fprintf(os.Stderr, "用户名至少 3 位\n") - }, -} - -func readPassword() (string, error) { - retry := 0 - maxRetry := 3 - for { - fmt.Print("密码: ") - b, err := terminal.ReadPassword(int(syscall.Stdin)) - if err != nil { - return "", err - } - password := string(b) - if len(password) >= minAccountSize { - fmt.Println() - return password, nil - } - retry++ - if retry >= 3 { - return "", fmt.Errorf("密码格式错误") - } - fmt.Printf("\n密码至少 6 位,请重试(%d/%d)\n", retry, maxRetry) - } -} - -func init() { - rootCmd.AddCommand(loginCmd) - loginCmd.Flags().StringVarP(&account, accountFlag, "u", "", "用户名(邮箱或手机号)") - loginCmd.Flags().StringVarP(&password, passwordFlag, "p", "", "密码") - loginCmd.MarkFlagRequired(accountFlag) -} - -func readTwoFACode() (string, error) { - fmt.Print("两步验证码: ") - b, err := terminal.ReadPassword(int(syscall.Stdin)) - if err != nil { - return "", fmt.Errorf("读取两步验证码失败, %v", err) - } - code := string(b) - if len(code) == 6 { - return code, nil - } - return "", fmt.Errorf("读取两步验为 6 位数字") -} diff --git a/cmd/pt.go b/cmd/pt.go deleted file mode 100644 index 44d4fea..0000000 --- a/cmd/pt.go +++ /dev/null @@ -1,83 +0,0 @@ -package cmd - -import ( - "bytes" - "github.com/golang/glog" - "github.com/spf13/cobra" - "text/template" -) - -const ( - tableFlag = "table" - alterFlag = "alter" - alterTemplate = `ALTER TABLE {{.table}} {{.alter}}` - ptTeamplate = ` -#!/usr/bin/env bash - - -DATABASE_USER= -DATABASE_PASSWORD= -DATABASE_HOST= -DATABASE_PORT= -DATABASE= - -pt-online-schema-change --user=$DATABASE_USER --password=$DATABASE_PASSWORD --alter "{{.alter}}" D=$DATABASE,t={{.table}} --host=$DATABASE_HOST:$DATABASE_PORT --execute --charset "utf8mb4" -` -) - -var table string -var alter string - -var ptCmd = &cobra.Command{ - Use: "pt", - Short: "生成 pt-online-schema-change 和 sql 文件", - Long: `使用命令行生成数据库表修改的生成 pt-online-schema-change 和 sql 文件 -示例:coding-cli pt -t sample -a "add column nickname varchar(32) default null comment '昵称' after id" -f sample_table_add_nickname_col -将会生成 YYYY-MM-DD-sample_table_add_nickname_col.pt.sh 和 YYYY-MM-DD-sample_table_add_nickname_col.pt.sql 俩个文件 -`, - Run: func(cmd *cobra.Command, args []string) { - - tpl, err := template.New("pt-bash").Parse(ptTeamplate) - if err != nil { - glog.Exitln("生成 pt-online-schema-change bash 文件失败") - } - var ptContent bytes.Buffer - err = tpl.Execute(&ptContent, map[string]string{ - alterFlag: alter, - tableFlag: table, - }) - if err != nil { - glog.Exitln("生成 pt-online-schema-change bash 文件失败") - } - - save(ptContent.String(), "pt", "sh") - - tpl, err = template.New("pt-sql").Parse(alterTemplate) - if err != nil { - glog.Exitln("生成 pt-online-schema-change sql 文件失败") - } - var sqlContent bytes.Buffer - err = tpl.Execute(&sqlContent, map[string]string{ - alterFlag: alter, - tableFlag: table, - }) - if err != nil { - glog.Exitln("生成 pt-online-schema-change sql 文件失败") - } - - save(sqlContent.String(), "pt", "sql") - }, -} - -func init() { - rootCmd.AddCommand(ptCmd) - - ptCmd.Flags().StringVarP(&alter, alterFlag, "a", "", "修改语句") - ptCmd.Flags().StringVarP(&table, tableFlag, "t", "", "表名") - ptCmd.Flags().StringVarP(&feature, featureFlag, "f", "", "功能名") - ptCmd.Flags().StringVarP(&output, "output", "o", "", "输出文件") - - ptCmd.MarkFlagRequired(alterFlag) - ptCmd.MarkFlagRequired(tableFlag) - ptCmd.MarkFlagRequired(featureFlag) -} diff --git a/cmd/release.go b/cmd/release.go deleted file mode 100644 index d6110d1..0000000 --- a/cmd/release.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © 2018 彭博 -// -// 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. - -package cmd - -import ( - "flag" - "fmt" - - "e.coding.net/codingcorp/coding-cli/pkg/api" - "e.coding.net/codingcorp/coding-cli/pkg/diff" - "github.com/golang/glog" - "github.com/spf13/cobra" -) - -var output string -var rtype string -var product string -var patch int8 -var config string - -// releaseCmd represents the release command -var releaseCmd = &cobra.Command{ - Use: "release 目标分支 或 源分支 目标分支", - Short: "创建版本发布", - Long: `创建版本发布 -为分支、提交或标签(简称 ref)创建版本发布,版本发布分为常规发布和紧急修复两类。 -示例命令:coding-cli release master enterprise-saas -o release-20181030.1-enterprise.md -p enterprise-saas -t normal -n 1 -c ~/.coding_release.yml -`, - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - source, target, err := parseArgs(args) - if err != nil { - glog.Exitln("解析目标分支参数异常,", err) - return - } - diff.Analysis( - product, - source, - target, - rtype, - patch, - output, - config, - ) - }, -} - -func parseArgs(args []string) (source string, target string, err error) { - argsSize := len(args) - if argsSize == 0 { - return "", "", fmt.Errorf("需提供分支名、提交或标签") - } - if argsSize == 1 { - target = args[0] - source = api.DefaultBranchCommitID() - } - if argsSize == 2 { - source = args[0] - target = args[1] - } - return -} - -func init() { - rootCmd.AddCommand(releaseCmd) - releaseCmd.Flags().StringVarP(&output, "output", "o", "", "保存到文件") - releaseCmd.Flags().StringVarP(&rtype, "type", "t", "normal", "发布类型,hotfix - 紧急修复或者 normal - 常规更新") - releaseCmd.Flags().StringVarP(&product, "product", "p", "enterprise-saas", "产品线,enterprise-saas 或者 professional") - releaseCmd.Flags().Int8VarP(&patch, "patch", "n", 1, "patch 序号") - releaseCmd.Flags().StringVarP(&config, "config", "c", "", "配置文件(默认为用户目录下的 .coding_release.yml 文件)") - flag.Parse() -} diff --git a/cmd/root.go b/cmd/root.go deleted file mode 100644 index 3cdd7a2..0000000 --- a/cmd/root.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright © 2018 彭博 -// -// 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. - -package cmd - -import ( - goflag "flag" - "fmt" - "os" - - "github.com/golang/glog" - homedir "github.com/mitchellh/go-homedir" - "github.com/spf13/cobra" - flag "github.com/spf13/pflag" - "github.com/spf13/viper" -) - -var cfgFile string - -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ - Use: "coding-cli", - Short: "Coding 企业版命令行工具", - Long: `Coding 企业版命令行工具, 主要功能和目标: - -- 使重复性的开发流程自动化:自动编写版本发布文档、合并请求的常规检查等 -- 简化项目管理过程:跟踪团队未合并代码进度等 -- 其他可以直接使用命令行快速处理的功能 -`, -} - -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - if err := rootCmd.Execute(); err != nil { - glog.Error(err) - os.Exit(1) - } -} - -func init() { - cobra.OnInitialize(initConfig) - // use glog in cobra see https://flowerinthenight.com/blog/2017/12/01/golang-cobra-glog - flag.CommandLine.AddGoFlagSet(goflag.CommandLine) -} - -// initConfig reads in config file and ENV variables if set. -func initConfig() { - if cfgFile != "" { - // Use config file from the flag. - viper.SetConfigFile(cfgFile) - } else { - // Find home directory. - home, err := homedir.Dir() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - // Search config in home directory with name ".coding-cli" (without extension). - viper.AddConfigPath(home) - viper.SetConfigName(".coding-cli") - } - - viper.AutomaticEnv() // read in environment variables that match - - // If a config file is found, read it in. - if err := viper.ReadInConfig(); err == nil { - fmt.Println("Using config file:", viper.ConfigFileUsed()) - } -} diff --git a/cmd/sql.go b/cmd/sql.go deleted file mode 100644 index 1d50be0..0000000 --- a/cmd/sql.go +++ /dev/null @@ -1,48 +0,0 @@ -package cmd - -import ( - "bytes" - "github.com/golang/glog" - "github.com/spf13/cobra" - "text/template" -) - -const ( - sqlTemplate = `{{.content}}` -) - -var sqlCmd = &cobra.Command{ - Use: "sql", - Short: "生成数据库执行的 SQL 文件", - Long: `使用命令行生成数据库执行的 SQL 文件 -示例:coding-cli sql -c " UPDATE sample SET nickname='tom' WHERE id = 1 " -f update_sample_nickname -将会生成 YYYY-MM-DD-update_sample_nickname.sql 文件 -`, - Run: func(cmd *cobra.Command, args []string) { - - tpl, err := template.New("sqlFile").Parse(sqlTemplate) - if err != nil { - glog.Exitln("生成 SQL 文件失败") - } - var sqlContent bytes.Buffer - err = tpl.Execute(&sqlContent, map[string]string{ - contentFlag: content, - }) - if err != nil { - glog.Exitln("生成 SQL 文件失败") - } - - save(sqlContent.String(), "update", "sql") - }, -} - -func init() { - rootCmd.AddCommand(sqlCmd) - - sqlCmd.Flags().StringVarP(&content, contentFlag, "c", "", "SQL 内容") - sqlCmd.Flags().StringVarP(&feature, featureFlag, "f", "", "功能名") - sqlCmd.Flags().StringVarP(&output, "output", "o", "", "输出文件") - - sqlCmd.MarkFlagRequired(contentFlag) - sqlCmd.MarkFlagRequired(featureFlag) -} diff --git a/hotfix.md b/hotfix.md deleted file mode 100644 index 0c09c35..0000000 --- a/hotfix.md +++ /dev/null @@ -1,97 +0,0 @@ -## ChangeLog - -- 任务日常定时提示过滤锁定用户和企业 #16775 -- 企业版一键导入个人版项目前端迭代 2 #16779 -- [Checklist] 个人版迁移企业版迭代 1,支持用户授权/查看/筛选项目 #16668 - - -## Diff - -https://codingcorp.coding.net/p/coding-dev/git/compare/4315fd3fc03909271299577c1a6d53acd39fb85d...4d2d8b8eb2ca6d0ee9d2028488eb834bd2635fef - -## Checklist - -### 发布类型 - -常规更新 - -### 负责人 - -@彭博 - - -### 发布服务 - -| 应用名称 | 发布镜像 | 执行顺序 | -| ---------- | ---------- | ---------- | -| enterprise-front | 20180917.2-enterprise-saas | 1 | -| e-coding | 20180917.2-enterprise-saas | 2 | -| e-scheduler | 20180917.2-enterprise-saas | 3 | - - -### 服务配置修改 - -e-coding/doc/mysql/migrate_script/migration-2018-09-12-import-projects.sql -``` --- ---------------------------- --- Table structure for import_project_histories --- ---------------------------- -CREATE TABLE `import_project_histories` ( - `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `platform_id` INT(10) UNSIGNED NOT NULL COMMENT '平台编号', - `project_id` INT(10) UNSIGNED DEFAULT NULL COMMENT '项目编号', - `project_name` VARCHAR(32) NOT NULL COMMENT '项目名称', - `operator_id` INT(10) UNSIGNED NOT NULL COMMENT '操作者编号', - `team_id` INT(10) UNSIGNED NOT NULL COMMENT '操作者所在企业编号', - `import_status` TINYINT(1) NOT NULL COMMENT '项目导入状态 0-未开始 1-导入中 2-成功 3-失败', - `import_created_at` DATETIME NOT NULL COMMENT '批次导入时间', - `created_at` DATETIME DEFAULT NULL COMMENT '开始时间', - `end_at` DATETIME DEFAULT NULL COMMENT '结束时间', - `tasks_count` INT(10) UNSIGNED NOT NULL COMMENT '导入任务数', - `files_count` INT(10) UNSIGNED NOT NULL COMMENT '导入文件数', - `wikis_count` INT(10) UNSIGNED NOT NULL COMMENT '导入wiki数', - `code_status` TINYINT(1) NOT NULL COMMENT '代码仓库导入状态 0-未开始 1-导入中 2-成功 3-失败', - `updated_at` DATETIME NOT NULL COMMENT '更新时间', - `error_message` VARCHAR(100) DEFAULT NULL COMMENT '错误消息', - `deleted_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00', - PRIMARY KEY (`id`), - KEY `idx_operator_team_id` (`operator_id`, `team_id`) -) - DEFAULT CHARSET = utf8mb4 - COLLATE = utf8mb4_unicode_ci - COMMENT ='导入项目历史'; - --- ---------------------------- --- Table structure for oauth_access_tokens --- ---------------------------- -CREATE TABLE `oauth_access_tokens`( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `user_id` INT(11) NOT NULL COMMENT 'user id', - `sid` VARCHAR(256) NOT NULL COMMENT 'cookies sid', - `platform_id` INT(11) NOT NULL COMMENT 'oauth platform id', - `access_token` VARCHAR(256) NOT NULL COMMENT 'oauth access_token', - `refresh_token` VARCHAR(256) DEFAULT NULL COMMENT 'oauth refresh_token', - `token_type` VARCHAR(32) DEFAULT NULL COMMENT 'oauth token type is bearer or mac' , - `scope` VARCHAR(128) DEFAULT NULL COMMENT 'oauth authorization scope' , - `expires_in` INT(11) DEFAULT NULL COMMENT 'oauth expires in', - `raw_response` MEDIUMTEXT DEFAULT NULL COMMENT 'oauth raw response', - `expires_at` DATETIME DEFAULT NULL COMMENT 'oauth expires at', - `created_at` DATETIME NOT NULL, - `updated_at` DATETIME NOT NULL , - `deleted_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' , - PRIMARY KEY(`id`) , - KEY `oauth_sid_idx`(`platform_id` , `sid`), - KEY `oauth_user_id_idx`(`platform_id` , `user_id`) - -) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户获取的平台授权'; - --- 新增Coding个人版平台 -INSERT INTO coding.provider_platforms (created_at, updated_at, deleted_at, cn_name, en_name) VALUES ('2018-09-06 15:11:00', '2018-09-06 15:11:00', '1970-01-01 00:00:00', 'CODING个人版', 'coding'); -``` - - -### 发布后 master 指向 - -``` -4315fd3fc03909271299577c1a6d53acd39fb85d -``` diff --git a/install.sh b/install.sh index 782ca15..f3c310d 100644 --- a/install.sh +++ b/install.sh @@ -13,11 +13,11 @@ # - CLI_ARCH (optional): use a specific value for ARCH (mostly for testing) # # You can install using this script: -# $ curl https://raw.githubusercontent.com/suuuy/coding-cli/master/install.sh | sh +# $ curl https://raw.githubusercontent.com/coding/coding-cli/master/install.sh | sh set -e -RELEASES_URL="https://github.com/suuuy/coding-cli/releases" +RELEASES_URL="https://github.com/coding/coding-cli/releases" downloadJSON() { url="$2" diff --git a/main.go b/main.go deleted file mode 100644 index 3389581..0000000 --- a/main.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright © 2018 彭博 -// -// 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. - -package main - -import "e.coding.net/codingcorp/coding-cli/cmd" - -func main() { - cmd.Execute() -} diff --git a/pkg/api/api.go b/pkg/api/api.go deleted file mode 100644 index c92e55e..0000000 --- a/pkg/api/api.go +++ /dev/null @@ -1,306 +0,0 @@ -package api - -import ( - "crypto/sha1" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "os/user" - "path" - "strings" - - "e.coding.net/codingcorp/coding-cli/pkg/model" - "github.com/golang/glog" - "github.com/parnurzeal/gorequest" -) - -const ( - host = "https://codingcorp.coding.net" - cookieFile = ".coding_release_rc" - error2fa = "two_factor_auth_code_not_empty" - form = "form" - referURI = "/api/user/codingcorp/project/coding-dev/resource_reference/%d" - defaultBranchURI = "/api/user/codingcorp/project/coding-dev/git/branches/default" - commitDetailURI = "/api/user/codingcorp/project/coding-dev/git/commit/%s" - diffURI = "/api/user/codingcorp/project/coding-dev/git/compare_v2?source=%s&target=%s&w=&prefix=" - mergeURI = "/api/user/codingcorp/project/coding-dev/git/merge/%d" - gitBlobURI = "/api/user/codingcorp/project/coding-dev/git/blob/%s" - currentUserURI = "/api/current_user" - diffTemplate = "/p/coding-dev/git/compare/%s...%s" - loginURI = "/api/v2/account/login" - twoFAAuthURI = "/api/check_two_factor_auth_code" -) - -func bind(str string, v interface{}) error { - result := model.Result{} - err := json.Unmarshal([]byte(str), &result) - if nil != err { - glog.Exitln("序列化 model.Result 失败", err) - } - if result.Code != 0 { - glog.Exitln("请求失败:", str) - } - - return json.Unmarshal(result.Data, v) -} - -//TwoFACode 俩步验证 -func TwoFACode(code string) *model.Result { - res, body, err := gorequest. - New(). - Post(apiURL(twoFAAuthURI)). - AddCookie(readCookie()). - Type(form). - SendMap(map[string]interface{}{ - "code": code, - }). - End() - if nil != err { - glog.Exitln("俩步验证失败") - } - model := model.Result{} - - if nil != bind(body, &model) { - glog.Exitln("序列化 model.Result 失败") - } - saveCookie(res) - return &model -} - -//Login 登录 -func Login(account string, password string, twoFACode func() (string, error)) { - res, body, err := gorequest. - New(). - Post(apiURL(loginURI)). - Type(form). - SendMap(map[string]interface{}{ - "account": account, - "password": sha1Password(password), - }). - End() - if nil != err { - glog.Exitln("登录失败", err) - } - m := model.Result{} - - if nil != bind(body, &m) { - glog.Exitln("序列化 model.Result 失败") - } - if m.Code != 0 { - if require2faCode(&m) { - code, err := twoFACode() - if nil != err { - glog.Errorln("读取俩步验证失败") - } - TwoFACode(code) - } - } else { - saveCookie(res) - } -} - -//CurrentUser 当前用户 -func CurrentUser() *model.User { - res, body, err := gorequest. - New(). - Get(apiURL(currentUserURI)). - AddCookie(readCookie()). - Type(form). - End() - if nil != err { - glog.Exitln("获取当前用户信息失败", err) - } - m := model.User{} - - if nil != bind(body, &m) { - glog.Exitln("序列化 model.User 失败", err) - } - saveCookie(res) - return &m -} - -//CommitID 获取提交 Hash -func CommitID(ref string) string { - res, body, err := gorequest. - New(). - Get(apiURL(fmt.Sprintf(commitDetailURI, ref))). - AddCookie(readCookie()). - Type(form). - End() - if nil != err { - glog.Exitln("获取 CommitID 失败") - } - m := model.ComplexCommit{} - - if nil != bind(body, &m) { - glog.Exitln("序列化 model.ComplexCommit 失败") - } - saveCookie(res) - return m.CommitDetail.CommitID -} - -//Diff 比较俩个提交 -func Diff(src string, target string) *model.Diff { - res, body, err := gorequest. - New(). - Get(apiURL(fmt.Sprintf(diffURI, src, target))). - AddCookie(readCookie()). - Type(form). - End() - if nil != err { - glog.Exitln("获取 Diff 失败") - } - - m := model.Diff{} - - if nil != bind(body, &m) { - glog.Exitln("序列化 model.Diff 失败") - } - saveCookie(res) - return &m -} - -//DefaultBranchCommitID 获取默认分支当前提交 -func DefaultBranchCommitID() string { - res, body, err := gorequest. - New(). - Get(apiURL(defaultBranchURI)). - AddCookie(readCookie()). - Type(form). - End() - if nil != err { - glog.Exitln("获取 CommitID 失败") - } - - m := model.Branch{} - - if nil != bind(body, &m) { - glog.Exitln("序列化 model.Branch 失败") - } - saveCookie(res) - return m.Name -} - -//Refer 获取关联资源 -func Refer(resourceID int) *model.Refer { - res, body, err := gorequest. - New(). - Get(apiURL(fmt.Sprintf(referURI, resourceID))). - AddCookie(readCookie()). - Type(form). - End() - if nil != err { - glog.Exitln("获取 Refer 失败") - } - - m := model.Refer{} - - if nil != bind(body, &m) { - glog.Exitln("序列化 model.Refer 失败") - } - saveCookie(res) - return &m -} - -//Merge 合并请求 -func Merge(mergeID int) *model.Merge { - res, body, err := gorequest. - New(). - Get(apiURL(fmt.Sprintf(mergeURI, mergeID))). - AddCookie(readCookie()). - Type(form). - End() - if nil != err { - glog.Exitln("获取 CommitID 失败") - } - - m := model.Merge{} - - if nil != bind(body, &m) { - glog.Exitln("序列化 model.Refer 失败") - } - saveCookie(res) - return &m -} - -//Blob 获取文件 Blob -func Blob(commitID string, n string) string { - encodedParams := url.PathEscape(fmt.Sprintf("%s/%s", commitID, n)) - res, body, err := gorequest. - New(). - Get(apiURL(fmt.Sprintf(gitBlobURI, encodedParams))). - AddCookie(readCookie()). - Type(form). - End() - if nil != err { - glog.Exitln("获取 File 失败") - } - - m := model.Blob{} - - if nil != bind(body, &m) { - glog.Exitln("序列化 model.Blob 失败") - } - saveCookie(res) - return m.File.Data -} - -//CompareURL 返回有 src 和 target 对应的 commit 组成的 diff 链接 -// 形如:https://codingcorp.coding.net/p/coding-dev/git/compare/master...enterprise-saas -func CompareURL(sourceHash string, targetHash string) string { - s := url.PathEscape(sourceHash) - t := url.PathEscape(targetHash) - return host + fmt.Sprintf(diffTemplate, s, t) -} - -func cookiePath() string { - usr, err := user.Current() - if err != nil { - glog.Exitln("无法读取用户目录下的 .coding_release_rc 文件") - } - return path.Join(usr.HomeDir, cookieFile) -} - -func saveCookie(res *http.Response) { - for _, c := range res.Cookies() { - if c.Name == "eid" { - err := ioutil.WriteFile(cookiePath(), []byte(c.Name+"="+c.Value), 0666) - if err != nil { - glog.Exitln("session Cookie 不存在") - } - } - } -} - -func readCookie() *http.Cookie { - b, err := ioutil.ReadFile(cookiePath()) - if err != nil { - glog.Exitln("~/.coding_release_rc Cookie 文件读取失败") - } - str := strings.Replace(string(b), "\n", "", -1) - cookiePair := strings.Split(str, "=") - if len(cookiePair) != 2 { - glog.Exitln("~/.coding_release_rc Cookie 不符合Cookie 格式 name=value ") - } - return &http.Cookie{ - Name: cookiePair[0], - Value: cookiePair[1], - } -} - -func require2faCode(result *model.Result) bool { - return result.Code > 1 && result.Msg[error2fa] != "" -} - -func sha1Password(password string) string { - h := sha1.New() - io.WriteString(h, password) - return fmt.Sprintf("%x", h.Sum(nil)) -} - -func apiURL(uri string) string { - return host + uri -} diff --git a/pkg/config/config.go b/pkg/config/config.go deleted file mode 100644 index c71785d..0000000 --- a/pkg/config/config.go +++ /dev/null @@ -1,46 +0,0 @@ -package config - -import ( - "io/ioutil" - "os/user" - "path" - - "github.com/golang/glog" - "gopkg.in/yaml.v2" -) - -const defaultConfigFile = ".coding_release.yml" - -type Config struct { - EID string `yaml:"eid,omitempty"` - Service []struct { - Name string `yaml:name",omitempty"` - Migrate string `yaml:"migrate,omitempty"` - Source []string `yaml:source",flow"` - } -} - -func Load(filePath string) *Config { - file := configPath(filePath) - b, err := ioutil.ReadFile(file) - if nil != err { - glog.Exitln(file, " 文件读取失败") - } - conf := Config{} - err = yaml.Unmarshal(b, &conf) - if nil != err { - glog.Exitln(file, " 文件解析失败") - } - return &conf -} - -func configPath(filePath string) string { - usr, err := user.Current() - if err != nil { - glog.Exitln("无法读取用户目录") - } - if len(filePath) <= 0 { - return path.Join(usr.HomeDir, defaultConfigFile) - } - return filePath -} diff --git a/pkg/diff/diff-template.go b/pkg/diff/diff-template.go deleted file mode 100644 index 3cf4c6c..0000000 --- a/pkg/diff/diff-template.go +++ /dev/null @@ -1,84 +0,0 @@ -package diff - -const diffTemplate = ` -# 更新日期 -{{.Date}} - -# ChangeLog - -{{range .Changes}}- {{.Title}}{{range .TaskIDs}} #{{.}}{{end}} #{{.MergeID}} -{{end}} - -# Diff - -{{.CompareURL}} - -# 发布类型 - -{{if .Hotfix}}Hotfix{{else}}常规更新{{end}} - -# 负责人 - -@{{.Principal}} - -# 版本规划 - -{{if .Milestone}}{{.Milestone}}{{else}}无{{end}} - ----------- - -# Staging 更新服务和配置修改 - -## Staging 更新服务 - -| 应用名称 | 发布镜像 | 执行顺序 | -| ---------- | ---------- | ---------- | -{{range $index, $element := .Staging.Service}}| {{.Name}} | {{$.Release}} | {{(inc $index)}} | -{{end}} - -## Staging 服务配置和数据库更新 -{{range .Staging.Service}} -{{if (len .Migration) ge 0}} -### 服务名: {{.Name}} -{{range .Migration}} -###### 文件: {{.ScriptName}} -` + "`" + "`" + "`" + ` -{{.Script}} -` + "`" + "`" + "`" + ` -{{end}} -{{end}} -{{end}} - -------------- - -# Production 更新服务和配置修改 - -## Production 更新服务 - -| 应用名称 | 发布镜像 | 执行顺序 | -| ---------- | ---------- | ---------- | -{{range $index, $element := .Prod.Service}}| {{.Name}} | {{$.Release}} | {{(inc $index)}} | -{{end}} - -## Production 服务配置和数据库更新 - -{{range $.Prod.Service}} -{{if (len .Migration) ge 0}} -### 服务名: {{.Name}} -{{range .Migration}} -###### 文件: {{.ScriptName}} -` + "`" + "`" + "`" + ` -{{.Script}} -` + "`" + "`" + "`" + ` -{{end}} -{{end}} -{{end}} - ------------ - -# 发布后 master 指向 - -` + "`" + "`" + "`" + ` -{{.PostMaster}} -` + "`" + "`" + "`" + ` -` diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go deleted file mode 100644 index 7d7d2b5..0000000 --- a/pkg/diff/diff.go +++ /dev/null @@ -1,255 +0,0 @@ -package diff - -import ( - "fmt" - "io" - "os" - "regexp" - "strconv" - "strings" - "text/template" - "time" - - "e.coding.net/codingcorp/coding-cli/pkg/api" - "e.coding.net/codingcorp/coding-cli/pkg/config" - "e.coding.net/codingcorp/coding-cli/pkg/model" - "github.com/golang/glog" -) - -var funcMap = template.FuncMap{ - "inc": func(i int) int { - return i + 1 - }, -} - -//Diff 分析 commit diff 请求 -type Diff struct { - source string - target string - product string - mode string - patch int8 - sourceHash string - targetHash string - diff model.Diff - context model.Context - conf config.Config -} - -//Run post request and analysis commit -func Analysis( - product string, - source string, - target string, - t string, - patch int8, - output string, - configFile string, -) { - c := Diff{ - product: product, - source: source, - target: target, - mode: t, - patch: patch, - } - - c.context.Date = time.Now().Format("2006-01-02") - - c.conf = *config.Load(configFile) - - c.context.Master = c.getSourceHash() - c.context.PostMaster = c.getTargetHash() - - if !c.isClean() { - glog.Exitln("基于目标分支对比源分支,有目标分支不存在的提交,请使用 merge 或者 rebase 操作保持分支差异干净") - } - - c.currentUser() - c.generateTag() - c.compare() - c.services() - - f := os.Stdout - if len(output) > 0 { - f, _ = os.Create(output) - c.save(f) - } -} - -func (c *Diff) getSourceHash() string { - c.sourceHash = c.source - if len(c.source) != 40 { - c.sourceHash = api.CommitID(c.source) - } - return c.sourceHash -} - -func (c *Diff) getTargetHash() string { - c.targetHash = c.target - if len(c.target) != 40 { - c.targetHash = api.CommitID(c.target) - } - return c.targetHash -} - -func (c *Diff) compare() { - - glog.Infof("正在创建基于 %s(%s)...%s(%s) 的版本发布", c.sourceHash, c.source, c.targetHash, c.target) - - // 变更记录 - c.changelogs() - glog.Infof("此版本包含 %d 个主要改动", len(c.context.Changes)) - - // 网页版本对比链接 - c.context.CompareURL = c.compareURL() -} - -//generateTag 生成发布版本标签 -func (c *Diff) generateTag() { - today := time.Now().Format("20060102") - c.context.Release = fmt.Sprintf("%s.%d-%s", today, c.patch, c.product) -} - -func (c *Diff) currentUser() { - user := api.CurrentUser() - c.context.CurrentUser = *user - c.context.Principal = user.Name -} - -func (c *Diff) isClean() bool { - c.diff = *api.Diff(c.targetHash, c.sourceHash) - return len(c.diff.Commits) <= 0 -} - -func (c *Diff) changelogs() { - c.diff = *api.Diff(c.sourceHash, c.targetHash) - changelogs := make([]model.ChangeLog, 0) - for _, commit := range c.diff.Commits { - msg := commit.AllMessage - messages := strings.Split(msg, "\n") - title, _, mergeID := filterTitleAndURL(messages) - if len(title) == 0 { - continue - } - title = strings.Replace(title, "Merge Request: ", "", 1) - cl := model.ChangeLog{Title: title} - if mergeID != 0 { - cl.MergeID = mergeID - } - merge := api.Merge(cl.MergeID) - cl.Merge = *merge - glog.Infof("提取到合并请求 #%d - %s", mergeID, merge.MergeRequest.Title) - ref := api.Refer(cl.MergeID) - if ref.Task != nil { - taskIDs := make([]int, 0) - for _, t := range ref.Task { - taskIDs = append(taskIDs, t.Code) - } - cl.TaskIDs = taskIDs - } - changelogs = append(changelogs, cl) - } - c.context.Changes = changelogs -} - -func (c *Diff) compareURL() string { - return api.CompareURL(c.sourceHash, c.targetHash) -} - -func (c *Diff) services() { - paths := c.paths() - services := make([]model.Service, 0) - for _, service := range c.conf.Service { - changes := make([]model.Path, 0) - for _, s := range service.Source { - matches := match(paths, s) - changes = append(changes, matches...) - } - if len(changes) > 0 { - services = append(services, model.Service{ - Name: service.Name, - Migrate: service.Migrate, - ChangeFiles: changes, - }) - } - } - - for i := range services { - s := &services[i] - s.Migration = make([]model.Migration, 0) - for _, f := range s.ChangeFiles { - if strings.HasPrefix(f.Path, s.Migrate) && len(s.Migrate) > 0 { - s.Migration = append(s.Migration, model.Migration{ - ScriptName: f.Path, - Script: api.Blob(c.targetHash, f.Name), - }) - } - } - } - - c.context.Staging.Service = services - c.context.Staging.Name = "Staging" - - c.context.Prod.Service = services - c.context.Prod.Name = "Production" -} - -func (c *Diff) paths() []model.Path { - paths := make([]model.Path, 0) - for _, p := range c.diff.DiffStat.Paths { - paths = append(paths, p) - } - return paths -} - -func (c *Diff) save(o io.Writer) { - outputTpl, err := template.New("release.md").Funcs(funcMap).Parse(diffTemplate) - - if err != nil { - glog.Exitln("构建输出文件模板失败, ", err) - } - - err = outputTpl.Execute(o, c.context) - if err != nil { - glog.Exitln("执行模板失败, ", err) - } -} - -var mergeRequestIDReg = regexp.MustCompile(`merge/(\d+)`) - -func mergeRequestID(msg string) int { - matches := mergeRequestIDReg.FindStringSubmatch(msg) - if len(matches) == 2 { - id, err := strconv.Atoi(matches[1]) - if err != nil { - return 0 - } - return id - } - return 0 -} - -func filterTitleAndURL(messages []string) (title string, url string, mergeID int) { - for _, msg := range messages { - if strings.Index(msg, "Merge Request") != -1 { - title = msg - continue - } - if strings.Index(msg, "URL") != -1 { - mergeID = mergeRequestID(msg) - url = msg - } - } - return title, url, mergeID -} - -func match(files []model.Path, prefix string) []model.Path { - paths := make([]model.Path, 0) - for _, f := range files { - if strings.HasPrefix(f.Path, prefix) { - paths = append(paths, f) - } - } - return paths -} diff --git a/pkg/model/README.md b/pkg/model/README.md deleted file mode 100644 index 5459313..0000000 --- a/pkg/model/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# 将 JSON 转换为 Model - -使用 [JSON-to-Go](https://mholt.github.io/json-to-go/) 在线工具可直接将 JSON 数据转换为 Go Struct - -![图片](https://dn-coding-net-production-pp.codehub.cn/b3167f35-c4f1-4cfe-8acd-828b6cb518ca.png) \ No newline at end of file diff --git a/pkg/model/blob.go b/pkg/model/blob.go deleted file mode 100644 index d5f89f9..0000000 --- a/pkg/model/blob.go +++ /dev/null @@ -1,23 +0,0 @@ -package model - -type File struct { - Data string `json:"data"` - Lang string `json:"lang"` - Size int `json:"size"` - Previewed bool `json:"previewed"` - LastCommitMessage string `json:"lastCommitMessage"` - LastCommitDate int64 `json:"lastCommitDate"` - LastAuthorDate int64 `json:"lastAuthorDate"` - LastCommitID string `json:"lastCommitId"` - LastCommitter Committer `json:"lastCommitter"` - Mode string `json:"mode"` - Path string `json:"path"` - Name string `json:"name"` -} - -type Blob struct { - Ref string `json:"ref"` - File File `json:"file"` - IsHead bool `json:"isHead"` - CanEdit bool `json:"can_edit"` -} diff --git a/pkg/model/branch.go b/pkg/model/branch.go deleted file mode 100644 index 607be16..0000000 --- a/pkg/model/branch.go +++ /dev/null @@ -1,10 +0,0 @@ -package model - -type Branch struct { - Name string `json:"name"` - LastCommit ShortCommit `json:"last_commit"` - IsDefaultBranch bool `json:"is_default_branch"` - IsProtected bool `json:"is_protected"` - DenyForcePush bool `json:"deny_force_push"` - ForceSquash bool `json:"force_squash"` -} diff --git a/pkg/model/commit.go b/pkg/model/commit.go deleted file mode 100644 index e78ce80..0000000 --- a/pkg/model/commit.go +++ /dev/null @@ -1,52 +0,0 @@ -package model - -type Committer struct { - Name string `json:"name"` - Email string `json:"email"` - Avatar string `json:"avatar"` - Link string `json:"link"` -} - -type Commit struct { - ShortCommit - FullMessage string `json:"fullMessage"` - AllMessage string `json:"allMessage"` - Committer Committer `json:"committer"` - NotesCount int `json:"notesCount"` - RawMessage string `json:"rawMessage"` -} - -type ShortCommit struct { - ShortMessage string `json:"shortMessage"` - CommitID string `json:"commitId"` - CommitTime int64 `json:"commitTime"` -} - -type ComplexCommit struct { - CommitComments []interface{} `json:"commitComments"` - CommitDetail struct { - DiffStat DiffStat `json:"diffStat"` - FullMessage string `json:"fullMessage"` - ShortMessage string `json:"shortMessage"` - AllMessage string `json:"allMessage"` - CommitID string `json:"commitId"` - CommitTime int64 `json:"commitTime"` - Committer User `json:"committer"` - NotesCount int `json:"notesCount"` - RawMessage string `json:"rawMessage"` - } `json:"commitDetail"` - Marks []Mark `json:"marks"` -} - -type Mark struct { - DepotID int `json:"depot_id"` - Sha string `json:"sha"` - MarkableType string `json:"markable_type"` - MarkableID int `json:"markable_id"` - Icon string `json:"icon"` - Name string `json:"name"` - Description string `json:"description"` - URL string `json:"url"` - Status int `json:"status"` - CreatedAt int64 `json:"created_at"` -} diff --git a/pkg/model/context.go b/pkg/model/context.go deleted file mode 100644 index 0f2eebc..0000000 --- a/pkg/model/context.go +++ /dev/null @@ -1,45 +0,0 @@ -package model - -//Context Release 上下文 -type Context struct { - CurrentUser User - Changes []ChangeLog - CompareURL string - Hotfix bool - Principal string - Date string - Milestone string - Release string - Staging Deploy - Prod Deploy - Master string - PostMaster string -} - -//ChangeLog 包含 Merge Request 标题、Merge Request 完整信息以及任务完整信息 -type ChangeLog struct { - Title string - MergeID int - TaskIDs []int - Merge Merge -} - -//Migration 需要执行的 sql 或者 配置改动 -type Migration struct { - ScriptName string - Script string -} - -//Service 更新的服务 -type Service struct { - Name string - Migrate string - ChangeFiles []Path - Migration []Migration -} - -//Deploy 部署环境 -type Deploy struct { - Name string - Service []Service -} diff --git a/pkg/model/diff.go b/pkg/model/diff.go deleted file mode 100644 index 235bba1..0000000 --- a/pkg/model/diff.go +++ /dev/null @@ -1,31 +0,0 @@ -package model - -type Diff struct { - Ref2Sha string `json:"ref2Sha"` - Commits []Commit `json:"commits"` - Ref1Sha string `json:"ref1Sha"` - Ref2 string `json:"ref2"` - Ref1 string `json:"ref1"` - DiffStat DiffStat `json:"diffStat"` -} - -type Path struct { - ChangeType string `json:"changeType"` - Insertions int `json:"insertions"` - Deletions int `json:"deletions"` - Name string `json:"name"` - Path string `json:"path"` - Size int `json:"size"` - Mode int `json:"mode"` - ObjectID string `json:"objectId"` - CommitID string `json:"commitId"` -} - -type DiffStat struct { - Paths []Path `json:"paths"` - CommitID string `json:"commitId"` - OldSha string `json:"oldSha"` - NewSha string `json:"newSha"` - Insertions int `json:"insertions"` - Deletions int `json:"deletions"` -} diff --git a/pkg/model/merge.go b/pkg/model/merge.go deleted file mode 100644 index 73c202f..0000000 --- a/pkg/model/merge.go +++ /dev/null @@ -1,45 +0,0 @@ -package model - -// Merge 包含 Merge Request 的基本信息以及 Code Review 相关辅助信息 -type Merge struct { - AuthorCanEdit bool `json:"author_can_edit"` - CanEditSrcBranch bool `json:"can_edit_src_branch"` - CanMergeHint string `json:"can_merge_hint"` - SrcBranchExists bool `json:"src_branch_exists"` - TargetIsProtected bool `json:"target_is_protected"` - LineNotes []interface{} `json:"line_notes"` - IsReviewer bool `json:"is_reviewer"` - CanEdit bool `json:"can_edit"` - MergeRequest struct { - MergedSha string `json:"merged_sha"` - DiffStat DiffStat `json:"diffStat"` - HTMLDiff string `json:"htmlDiff"` - Commits []Commit `json:"commits"` - Body string `json:"body"` - BodyPlan string `json:"body_plan"` - SourceSha string `json:"source_sha"` - TargetSha string `json:"target_sha"` - BaseSha string `json:"base_sha"` - Conflicts []interface{} `json:"conflicts"` - ID int `json:"id"` - SrcBranch string `json:"srcBranch"` - DesBranch string `json:"desBranch"` - Title string `json:"title"` - Iid int `json:"iid"` - MergeStatus string `json:"merge_status"` - Path string `json:"path"` - CreatedAt int64 `json:"created_at"` - UpdatedAt int64 `json:"updated_at"` - Author User `json:"author"` - ActionAuthor User `json:"action_author"` - ActionAt int64 `json:"action_at"` - Granted int `json:"granted"` - GrantedBy User `json:"granted_by"` - CommentCount int `json:"comment_count"` - Marks []interface{} `json:"marks"` - Reminded bool `json:"reminded"` - } `json:"merge_request"` - CanMerge bool `json:"can_merge"` - CanGrant bool `json:"can_grant"` - Labels []interface{} `json:"labels"` -} diff --git a/pkg/model/refer.go b/pkg/model/refer.go deleted file mode 100644 index bb803c6..0000000 --- a/pkg/model/refer.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -type Refer struct { - Task []struct { - TargetProjectID int `json:"target_project_id"` - TargetProjectName string `json:"target_project_name"` - Code int `json:"code"` - TargetType string `json:"target_type"` - TargetID int `json:"target_id"` - Title string `json:"title"` - Link string `json:"link"` - Status int `json:"status"` - } `json:"Task"` -} diff --git a/pkg/model/result.go b/pkg/model/result.go deleted file mode 100644 index 7b5ac15..0000000 --- a/pkg/model/result.go +++ /dev/null @@ -1,10 +0,0 @@ -package model - -import "encoding/json" - -// Result 包裹着所有 JSON 返回值 -type Result struct { - Code int `json:"code"` - Data json.RawMessage - Msg map[string]string `json:"msg"` -} diff --git a/pkg/model/task.go b/pkg/model/task.go deleted file mode 100644 index db98b8f..0000000 --- a/pkg/model/task.go +++ /dev/null @@ -1,21 +0,0 @@ -package model - -type Task struct { - Markdown string `json:"markdown"` - Description string `json:"description"` - History TaskHistory `json:"history"` -} - -type TaskHistory struct { - Owner User `json:"owner"` - Data struct { - OwnerID int `json:"owner_id"` - TaskID int `json:"task_id"` - ActivityID int `json:"activity_id"` - CreatedAt int64 `json:"created_at"` - Action int `json:"action"` - Description string `json:"description"` - ID int `json:"id"` - } `json:"data"` - IsNewest bool `json:"is_newest"` -} diff --git a/pkg/model/user.go b/pkg/model/user.go deleted file mode 100644 index 5b1296f..0000000 --- a/pkg/model/user.go +++ /dev/null @@ -1,44 +0,0 @@ -package model - -// User 包含用户的基本信息 -type User struct { - TagsStr string `json:"tags_str"` - Tags string `json:"tags"` - Job int `json:"job"` - Sex int `json:"sex"` - Phone string `json:"phone"` - Birthday string `json:"birthday"` - Location string `json:"location"` - Company string `json:"company"` - Slogan string `json:"slogan"` - Website string `json:"website"` - Introduction string `json:"introduction"` - Avatar string `json:"avatar"` - Gravatar string `json:"gravatar"` - Lavatar string `json:"lavatar"` - CreatedAt int64 `json:"created_at"` - LastLoginedAt int64 `json:"last_logined_at"` - LastActivityAt int64 `json:"last_activity_at"` - GlobalKey string `json:"global_key"` - Name string `json:"name"` - NamePinyin string `json:"name_pinyin"` - UpdatedAt int64 `json:"updated_at"` - Path string `json:"path"` - Status int `json:"status"` - Email string `json:"email"` - IsMember int `json:"is_member"` - ID int `json:"id"` - PointsLeft float64 `json:"points_left"` - FollowsCount int `json:"follows_count"` - FansCount int `json:"fans_count"` - TweetsCount int `json:"tweets_count"` - PhoneCountryCode string `json:"phone_country_code"` - Country string `json:"country"` - Followed bool `json:"followed"` - Follow bool `json:"follow"` - IsPhoneValidated bool `json:"is_phone_validated"` - EmailValidation int `json:"email_validation"` - PhoneValidation int `json:"phone_validation"` - TwofaEnabled int `json:"twofa_enabled"` - IsWelcomed bool `json:"is_welcomed"` -}