L
L
i
i
r
r
o
o
u
u
s
s
c
c
o
o
d
d
i
i
n
n
g
g
git-commit项目的Docker部署

Docker部署git-commit项目

上次说了本地如何运行gitCommit这个项目,现在我们准备将其使用部署到服务器上,部署方法为Docker容器部署。

对于docker,相当于是一个新的开发环境,所以你进行git操作一定要避免代码冲突,这里提出的一种解决方式是先add和commit自己本地的代码,然后再push远程分支到本地的另外一个分支,然后接着切换分支,然后进行ticker里面的循环操作

main文件修改

  1. 将项目路径修改为了 /app,即 projectPath = "/app" ,/app是Docker容器里面的的工作目录

移动到工作目录:/app

dockerfile 复制代码
WORKDIR /app
  1. 在go代码里面获取了容器里面设置的环境变量,即GITHUB_PAT
go 复制代码
githubPAT := os.Getenv("GITHUB_PAT") 

mian.go

go 复制代码
package main

import (
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"time"
)

const (
	projectPath      = "/app" //把项目目录设置为容器的工作目录
	fileName         = "fish.txt"
	message          = "我没摸鱼"
	prefix           = "今天又摸鱼了鸭"
	maxCommitsPerDay = 5  // 每天最大提交次数
	startHour        = 9  // 允许操作的开始小时(24小时制)
	endHour          = 24 // 允许操作的结束小时(24小时制)
)

func main() {
	//首先得到正确的文件路径 用这个函数的好处是不用管操作系统的分隔符 管它是/还是\ go语言直接帮你解决
	path := filepath.Join(projectPath, fileName)

	//检测该文件是否存在
	if ifExist := checkFileExist(path); ifExist == false {
		if err := createFile(path); err != nil {
			log.Printf("createFile(path) failed,err%v", err)
			return
		}
		log.Println("createFile(path) success")
	}

	// 定时逻辑
	var commitTimes = 0
	now := getChineseTime()
	today := now.Day()

	//格式化时间
	interval := getRandomInterval()
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	//初始化仓库
	if err := initAndRemoteAdd(projectPath); err != nil {
		log.Printf("initAndRemoteAdd(projectPath) failed,err:%v", err)
		return
	}

	//第一次获取最大push次数
	maxCommitsPerDay := getRandomPushTimes()
	//根据条件来判断 对文件的操作
	for range ticker.C {
		log.Printf("%d chances today", maxCommitsPerDay)
		if err := modifyFile(path); err != nil {
			log.Printf("modifyFile(fileName) failed,err:%v", err)
			return
		}
		log.Println("modifyFile(fileName) success")

		now = getChineseTime()
		currentHour := now.Hour()
		currentDay := now.Day()

		// 如果跨越了一天,重置计数器和日期
		if today != currentDay {
			commitTimes = 0
			today = currentDay
			maxCommitsPerDay = getRandomPushTimes()
		}

		// 检查是否在允许的时间段内
		if currentHour >= startHour && currentHour < endHour && commitTimes < maxCommitsPerDay {
			//提交git 代码待补充
			if err := commitChanges(projectPath, message, commitTimes); err != nil {
				log.Printf("commitChanges(path,message) failed,err:%v", err)
				return
			}
			// 提交后增加计数器
			commitTimes++
		}
		// 更新间隔
		interval = getRandomInterval()
		ticker.Reset(interval)
	}
}

// 检测文件是否存在
func checkFileExist(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}

// 创建文件
func createFile(path string) error {
	//先创建文件 没有文件会默认创建一个
	fPointer, err := os.OpenFile(path, os.O_CREATE, 0644)
	if err != nil {
		log.Printf("create file failed,err:%v", err)
		return err
	}
	defer fPointer.Close()
	return err
}

// 修改文件
func modifyFile(path string) error {
	content := fmt.Sprintf("%s %d", prefix, time.Now().UnixNano())
		fPointer, err := os.OpenFile(path, os.O_TRUNC|os.O_WRONLY, 0666) // os.O_WRONLY 仅写权限  |os.O_TRUNC 清空
	defer fPointer.Close()
	if err != nil {
		return err
	}
	_, err = fPointer.Write([]byte(content))
	return err
}

// 初始化仓库
func initAndRemoteAdd(projectPath string) error {
	//init
	if err := gitCommand(projectPath, "init"); err != nil {
		return err
	}

	//git add . --> 因为后续要切换分支 所以当前的分支必须add
	if err := gitCommand(projectPath, "add", "."); err != nil {
		return err
	}
	//git commit . --> 因为后续要切换分支 所以当前的分支必须commit
	if err := gitCommand(projectPath, "commit", "-m", "commit本地分支"); err != nil {
		return err
	}

	//remote add
	githubPAT := os.Getenv("GITHUB_PAT") //这里是因为要使用docker 所以设置了这个

	//拼接URL
	remoteURL := fmt.Sprintf("https://%s@github.com/username/Script.git", githubPAT)

	//这里可以不进行错误处理,一般报错就是已经有了这个远程仓库,但还是建议第一次写一下,后续改回来
	_ = gitCommand(projectPath, "remote", "add", "origin", remoteURL)

	//强行pull分支
	_ = gitCommand(projectPath, "pull", "--force", "origin", "main:main")

	//切换到main分支
	_ = gitCommand(projectPath, "checkout", "main")
	return nil
}

// push代码
func commitChanges(projectPath, message string, times int) error {
	//add
	if err := gitCommand(projectPath, "add", "."); err != nil {
		return err
	}
	//commit
	if err := gitCommand(projectPath, "commit", "-m", message); err != nil {
		return err
	}
	//push
	if err := gitCommand(projectPath, "push", "-u", "origin", "main:main"); err != nil {
		return err
	}
	log.Println(fmt.Sprintf("push %d times today", times+1))
	return nil
}

// 指令代码
func gitCommand(dir string, command ...string) (err error) {
	cmd := exec.Command("git", command...)
	cmd.Dir = dir
	var commandSql string
	for _, v := range command {
		commandSql += v + " "
	}
	output, err := cmd.CombinedOutput()
	if err != nil {
		log.Println(commandSql + "failed")
		log.Println("Command output:", string(output))
		return err
	}
	log.Println(commandSql + "success")
	return err
}

// 设置为中国时区
func getChineseTime() time.Time {
	loc, _ := time.LoadLocation("Asia/Shanghai")
	now := time.Now().In(loc)
	return now
}

// 生成种子
func newSeed() *rand.Rand {
	// 创建一个新的随机数生成器实例,为其设置种子
	src := rand.NewSource(time.Now().UnixNano())
	return rand.New(src)
}

// 得到随机时间间隔
func getRandomInterval() time.Duration {
	// 将分钟数转换为字符串,格式为"Xm"
	return time.Duration(newSeed().Intn(30)+150) * time.Minute
}

// 得到随机每天的push次数
func getRandomPushTimes() int {
	return newSeed().Intn(maxCommitsPerDay + 1)
}

docker部分

值得关注的点

  1. 设置了环境变量
dockerfile 复制代码
GITHUB_PAT=你自己的GitHub生成的token \
  1. 合并Run指令,以此来减少镜像构建层数
dockerfile 复制代码
FROM golang:alpine AS builder

# 为我们的镜像设置必要的环境变量,并进行其他设置
ENV GO111MODULE=on \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64 \
    GITHUB_PAT=你自己的GitHub生成的token \
    GOPROXY=https://goproxy.cn,direct

# 使用单个RUN命令执行多个命令以减少镜像层数
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \
    apk update && apk add --no-cache git && \
    git config --global user.name "lzh" && \
    git config --global user.email "2115883273@qq.com" && \
    go env -w GO111MODULE=on && \
    go env -w GOPROXY=https://goproxy.cn,direct

# 移动到工作目录:/app
WORKDIR /app

# 将代码复制到容器中
COPY . .
RUN go mod download

# 将我们的代码编译成二进制可执行文件 也可以在这里合并RUN指令,但是考虑到可读性和构建缓存的有效利用,通常保持这个步骤单独是有益的
RUN go build -o git_commit .

# 声明服务端口
EXPOSE 8080

# 需要运行的命令
ENTRYPOINT ["/app/git_commit"]

如何生成自己的GitHub token

生成自己的GitHubToken

GitHubToken

右边选择Tokens(classic),简单起见就全部勾选选项。

然后生成就可以了。

然后会出现一个token,务必保存,因为只可以看见一次

然后把Dockerfile里面的 GITHUB_PAT 替换成你的token就可以了

然后在项目目录下打开终端,依次运行

sh 复制代码
docker build -t git_commit .
docker run -p 8888:8888 -d --name git_commit git_commit
# 容器的端口映射可以自行设置

这样项目就成功在本地用Docker部署了

如果要在服务器上部署,只需要在服务器上安装Docker即可,然后把本地文件传输过去,然后运行以上的两行命令,即可完成服务器的部署。

注意
上传文件时应当删除本地的.git文件(隐藏文件,要自行设置文件可见)

设置文件可见