205 lines
4.3 KiB
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
|
|
}
|