pr-deployer/pkgs/services/services.go

205 lines
4.3 KiB
Go

package services
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"gitea.com/gitea/pr-deployer/pkgs/settings"
"github.com/cloudflare/cloudflare-go"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/google/go-github/v39/github"
)
func UpdateAndStartPullRequest(ctx context.Context, number int, sha string) error {
// 0 send commit status
if err := updateCommitStatus(ctx, number, sha, statusPending, "", ""); err != nil {
return err
}
// 1 download the git
if err := updateGitRepo(number); err != nil {
return err
}
// 2 change domain
if err := checkAndUpdateSubDomain(number); err != nil {
return err
}
// 3 build
if err := buildImage(ctx, number); err != nil {
return err
}
// 4 change reverse server
if err := runImage(ctx, number); err != nil {
return err
}
// 5 send commit status
if err := updateCommitStatus(ctx, number, sha, statusSuccess, "", fmt.Sprintf("https://try-pr-%d.gitea.io", number)); err != nil {
return err
}
return nil
}
func runImage(ctx context.Context, number int) error {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
return err
}
_, err = cli.ContainerExecCreate(ctx, "gitea/gitea:"+getImageTag(number), types.ExecConfig{
WorkingDir: getPRDir(number),
})
return err
}
func getRepoName() string {
return "gitea"
}
func getOwnerName() string {
return "go-gitea"
}
const (
statusPending = "pending"
statusSuccess = "success"
statusError = "error"
statusFailure = "failure"
)
// updateCommitStatus
func updateCommitStatus(ctx context.Context, number int, sha, status, desc, targetURL string) error {
c := github.NewClient(nil)
// pending, success, error, or failure
_, _, err := c.Repositories.CreateStatus(ctx, getOwnerName(), getRepoName(), sha, &github.RepoStatus{
State: github.String(status),
TargetURL: github.String(targetURL),
Description: github.String(desc),
Context: github.String("pr-deployer"),
// AvatarURL:
})
return err
}
func getPRDir(number int) string {
return filepath.Join(settings.CodeCacheDir, strconv.Itoa(number))
}
func updateGitRepo(number int) error {
p := getPRDir(number)
fmt.Println("clone code into", p)
// 1.1 git fetch origin +refs/heads/main
// 1.2 git checkout main
// 1.3 git fetch origin refs/pull/17422/head
// 1.4 git merge <xxxxxx>
st, err := os.Stat(p)
if err != nil {
if os.IsNotExist(err) {
_, err = git.PlainClone(p, false, &git.CloneOptions{
URL: "https://github.com/go-gitea/gitea",
InsecureSkipTLS: false,
ReferenceName: plumbing.NewRemoteReferenceName("", fmt.Sprintf("refs/pull/%d/head", number)),
SingleBranch: true,
})
if err != nil {
return err
}
}
return err
}
if !st.IsDir() {
return fmt.Errorf("%s is a file but not a dir", p)
}
_, err = git.PlainOpen(p)
if err != nil {
return err
}
return nil
}
func getImageTag(number int) string {
return fmt.Sprintf("pr-%d", number)
}
// buildImage build the docker image via Dockerfile
func buildImage(ctx context.Context, number int) error {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
return err
}
var buildContext io.Reader
if _, err = cli.ImageBuild(ctx, buildContext, types.ImageBuildOptions{
Tags: []string{getImageTag(number)},
}); err != nil {
return err
}
return nil
}
func checkAndUpdateSubDomain(number int) error {
api, err := cloudflare.New(settings.CloudflareToken, settings.CloudflareEmail)
if err != nil {
return err
}
zoneID, err := api.ZoneIDByName("gitea.io")
if err != nil {
return err
}
var found bool
var name = fmt.Sprintf("try-pr-%d.gitea.io", number)
foo := cloudflare.DNSRecord{Name: name}
recs, err := api.DNSRecords(context.Background(), zoneID, foo)
if err != nil {
return err
}
for _, r := range recs {
if name == r.Name {
found = true
break
}
}
if found {
// check ip address
return nil
}
asciiInput := cloudflare.DNSRecord{
Type: "A",
Name: name,
Content: settings.DomainIP,
TTL: 120,
//Priority: &priority,
//Proxied: &proxied,
}
_, err = api.CreateDNSRecord(context.Background(), zoneID, asciiInput)
if err != nil {
return err
}
return nil
}
func StopPullRequest(number int) error {
return nil
}