[cobra] Handwrite your first command-line scaffolding tool | cobra integrates go template to generate .drone.yml template through the command line through the terminal

Zero. Foreword

The open source framework used in this tutorial is as follows:

name open source address effect
Cobra command line tool https://github.com/spf13/cobra
Aurora font color https://github.com/logrusorgru/aurora
go-zero go-z framework template function https://github.com/zeromicro/go-zero

The complete source code of this project : https://github.com/ctra-wang/cobra-gen-drone

1. Getting to know Cobra for the first time

insert image description here

1. The role of Cobra

Overview : Cobra is a Golang package that provides a simple interface to create command-line programs. At the same time, Cobra is also an application program, which is used to generate an application framework to develop Cobra-based applications.
Representative works : docker, k8s, helmetc.

The detailed use will not be introduced in detail here

  • Recommended articles are as follows:

Official documentation: https://cobra.dev/

Detailed explanation of Cobra, a modern command line framework for Go language: https://jianghushinian.cn/2023/05/08/go-modern-command-line-framework-cobra-details/

Cobra Quick Start - Designed for command-line programs: https://blog.csdn.net/xcbeyond/article/details/119429114

  • Recommended videos are as follows:

Basic use of cobra

二、go template

Here is the template we want to render (the file ending in .tpl format).
By rendering the template, we can output the final file we want

1. go template use

Detailed use and instructions will not be introduced

Regarding template :
We can use .tpl files, or directly wrap them with `` strings for rendering. Mainly use the characteristics of { {}} double curly braces in go

3. Start the project

The entire project directory is as follows:
insert image description here

1. Create a project catalog

1.1. Create go.mod and cmd folders

go mod init xxxx

mkdir cmd

1.2, create the main file

package main

import (
	"app/cmd"
)

func main() {
    
    
	cmd.Execute()
}

1.3. Create cmd.go in the cmd folder

code show as below

package cmd

import (
	"fmt"
	"github.com/logrusorgru/aurora"
	"github.com/spf13/cobra"
	"os"
)

// droneCmd represents the drone command
var droneCmd = &cobra.Command{
    
    
	Use:   "drone",
	Short: "drone is very good",
	Long:  `创建drone的指令`,
	RunE:  DroneGenerator, //步骤一
}

var (
	//步骤三
	DroneName   string
	GoPrivate   string
	ServiceName string
	ServiceType string
	GitBranch   string
	Registry    string
	Repo        string
	Tag         string
)

func Execute() {
    
    
	if err := droneCmd.Execute(); err != nil {
    
    
		fmt.Println(aurora.Red(err.Error()))
		os.Exit(1)
	}
}

func init() {
    
    
	// drone --droneName="base" --go_private="gitee.com" --service_name="baserpc.go" --service_type="rpc" --gitBranch="master" --registry="registry.cn-beijing.aliyuncs.com" --repo="registry.cn-beijing.aliyuncs.com/ctra_test/ctra-go-zhiye-rpc" --tag="latest"
	// drone -d="base" -g="gitee.com" -s="baserpc.go" -x="rpc" -b="master" -r="registry.cn-beijing.aliyuncs.com" -o="registry.cn-beijing.aliyuncs.com/ctra_test/ctra-go-zhiye-rpc" -t="latest"

	// 步骤二
	droneCmd.Flags().StringVarP(&DroneName, aurora.Yellow("droneName").String(), "d", "", aurora.Green(`The Drone name`).String())
	droneCmd.Flags().StringVarP(&GoPrivate, "go_private", "g", "", aurora.Green(`Go private (default "gitee.com")`).String())
	droneCmd.Flags().StringVarP(&ServiceName, "service_name", "s", "", aurora.Green(`The service name of the project`).String())
	droneCmd.Flags().StringVarP(&ServiceType, "service_type", "x", "", aurora.Green(`The service type, such as rpc | api`).String())
	droneCmd.Flags().StringVarP(&GitBranch, "gitBranch", "b", "", `The branch of the remote repo, it does work with --remote (default "master")`)
	droneCmd.Flags().StringVarP(&Registry, "registry", "r", "", `The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority  
    The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure`)
	droneCmd.Flags().StringVarP(&Repo, "repo", "o", "", aurora.Green(`The project git repository`).String())
	droneCmd.Flags().StringVarP(&Tag, "tag", "t", "", aurora.Green("Git tag (default \"latest\")").String())

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	droneCmd.PersistentFlags().String("droneName", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// droneCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

1.4. Create drone.go in the cmd folder

package cmd

import (
	_ "embed"
	"fmt"
	"github.com/logrusorgru/aurora"
	"github.com/spf13/cobra"
	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
	"os"
	"strings"
	"text/template"
)

var (
	//go:embed drone.tpl
	UsageTpl string
)

type Drone struct {
    
    
	//步骤三
	DroneName   string
	GoPrivate   string
	ServiceName string
	ServiceType string
	GitBranch   string
	Registry    string
	Repo        string
	Tag         string
}

func DroneGenerator(_ *cobra.Command, _ []string) error {
    
    

	// 步骤四
	// 对所有的传入的参数进行一一判断
	dronename := DroneName
	if len(dronename) == 0 {
    
    
		dronename = "dronegen-greet"
	}

	goprivate := GoPrivate
	fmt.Println(len(strings.Split(goprivate, ".")))
	if len(strings.Split(goprivate, ".")) <= 1 {
    
    
		return fmt.Errorf("error go private!")
	}

	serviceName := ServiceName
	serviceType := ServiceType
	gitBranch := GitBranch
	registry := Registry
	repo := Repo
	tag := Tag

	file, err := os.Create("drone.yml")
	if err != nil {
    
    
		fmt.Println("文件创建失败:", err)
		return err
	} else {
    
    
		fmt.Println("文件创建成功!")
	}

	defer file.Close()

	text, err := pathx.LoadTemplate("dronegen", "drone.tpl", UsageTpl)
	if err != nil {
    
    
		fmt.Println("打开模板失败:", err)
		return err
	} else {
    
    
		fmt.Println("打开模板成功!")
	}

	t := template.Must(template.New("dronegen").Parse(text))
	return t.Execute(file, Drone{
    
    
		DroneName:   dronename,
		GoPrivate:   goprivate,
		ServiceName: serviceName,
		ServiceType: serviceType,
		GitBranch:   gitBranch,
		Registry:    registry,
		Repo:        repo,
		Tag:         tag,
	})
	fmt.Println(aurora.Green("Done."))

	return nil
}

1.5. Create drone.tpl template file

kind: pipeline
type: docker
name: {
    
    {
    
    .DroneName}}-{
    
    {
    
    .ServiceType}}
steps:
  - name: build-go
    image: golang:1.20.3
    depends_on: [clone]
    volumes:
      - name: go_cache
        path: /go/pkg/mod
    commands:
      - go env -w CGO_ENABLED=0
      - go env -w GOPROXY=https://goproxy.cn,direct
      - go env -w GOPRIVATE= {
    
    {
    
    .GoPrivate}}
      - go mod tidy && go build -trimpath -ldflags "-s -w" -o app {
    
    {
    
    .ServiceName}}.go

  - name: build-{
    
    {
    
    .ServiceType}}
    image: plugins/docker:20
    environment:
      DRONE_REPO_BRANCH: {
    
    {
    
    .GitBranch}}
    depends_on: [build-go]
    settings:
      dockerfile: Dockerfile
      registry: {
    
    {
    
    .Registry}}
      repo: {
    
    {
    
    .Repo}}:{
    
    {
    
    .Tag}}
      auto_tag: true
      insecure: true
      username:
        from_secret: docker_username
      password:
        from_secret: docker_password
trigger:
  ref:
    - refs/tags/*
    - refs/heads/master

volumes:
  - name: go_cache
    host:
      path: /root/.go/cache

2. How to debug in goland?

This kind of command line code debugging is completely different from the traditional main entry program debugging

2.1、Edit configurations

configure here
insert image description here

2.2、program argument

This is very important, if the instruction value we need to enter is not set here, the corresponding breakpoint cannot be entered

insert image description here

3. Compile and execute

3.1, compile the file into a binary file

go build main.go

3.2. Execution

long command

drone -d="base" -g="gitee.com" -s="baserpc.go" -x="rpc" -b="master" -r="registry.cn-beijing.aliyuncs.com" -o="registry.cn-beijing.aliyuncs.com/ctra_test/ctra-go-zhiye-rpc" -t="latest"

short command

drone --droneName="base" --go_private="gitee.com" --service_name="baserpc.go" --service_type="rpc" --gitBranch="master" --registry="registry.cn-beijing.aliyuncs.com" --repo="registry.cn-beijing.aliyuncs.com/ctra_test/ctra-go-zhiye-rpc" --tag="latest"

You can see that the drone.yml file has been generated
insert image description here

4. Pay attention to the big pit here

Take a look at the configuration page of Drone.
The default file name prefix of Configuration is点!!!

insert image description here

os.create()Functions in Golang

	file, err := os.Create("drone.yml")
	if err != nil {
    
    
		fmt.Println("文件创建失败:", err)
		return err
	} else {
    
    
		fmt.Println("文件创建成功!")
	}

If we use the default .drone.yml, we will not generate this file wherever we execute the compiled file! ! Ten Pit

file, err := os.Create(".drone.yml")

Guess you like

Origin blog.csdn.net/wanglei19891210/article/details/131847587