login with oauth2 & update commit status
This commit is contained in:
parent
1cd16acc2b
commit
2422b739f5
|
@ -1 +1,2 @@
|
||||||
pr-deployer
|
pr-deployer
|
||||||
|
config.yaml
|
|
@ -0,0 +1,18 @@
|
||||||
|
ServiceType: github
|
||||||
|
RepoURL: https://github.com/go-gitea/gitea
|
||||||
|
OAuth2:
|
||||||
|
ServerURL: https://github.com
|
||||||
|
ClientID: xxxxxx
|
||||||
|
ClientSecret: xxxxxxxx
|
||||||
|
CallbackURL: http://localhost:3001
|
||||||
|
|
||||||
|
Domain: localhost
|
||||||
|
DomainIP: localhost
|
||||||
|
WebhookSecretKey: ''
|
||||||
|
CodeCacheDir: './data'
|
||||||
|
CloudflareToken: ''
|
||||||
|
CloudflareEmail: ''
|
||||||
|
Proxy:
|
||||||
|
Enabled: false
|
||||||
|
ProxyURL: 'socks5:127.0.0.1:1080'
|
||||||
|
ProxyHosts: github.com
|
35
go.mod
35
go.mod
|
@ -3,37 +3,68 @@ module gitea.com/gitea/pr-deployer
|
||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
gitea.com/go-chi/session v0.0.0-20211013065435-7d334f340c09
|
||||||
github.com/cloudflare/cloudflare-go v0.26.0
|
github.com/cloudflare/cloudflare-go v0.26.0
|
||||||
|
github.com/docker/docker v20.10.10+incompatible
|
||||||
github.com/go-chi/chi/v5 v5.0.4
|
github.com/go-chi/chi/v5 v5.0.4
|
||||||
github.com/go-git/go-git/v5 v5.4.2
|
github.com/go-git/go-git/v5 v5.4.2
|
||||||
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/google/go-github/v39 v39.2.0
|
github.com/google/go-github/v39 v39.2.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/spf13/cobra v1.2.1
|
github.com/spf13/cobra v1.2.1
|
||||||
|
github.com/spf13/viper v1.8.1
|
||||||
github.com/unrolled/render v1.4.0
|
github.com/unrolled/render v1.4.0
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
github.com/Microsoft/go-winio v0.4.17 // indirect
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
||||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||||
|
github.com/containerd/containerd v1.5.7 // indirect
|
||||||
|
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||||
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
github.com/emirpasic/gods v1.12.0 // indirect
|
github.com/emirpasic/gods v1.12.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
github.com/go-git/gcfg v1.5.0 // indirect
|
github.com/go-git/gcfg v1.5.0 // indirect
|
||||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/imdario/mergo v0.3.12 // indirect
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||||
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||||
|
github.com/pelletier/go-toml v1.9.3 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/sergi/go-diff v1.1.0 // indirect
|
github.com/sergi/go-diff v1.1.0 // indirect
|
||||||
|
github.com/spf13/afero v1.6.0 // indirect
|
||||||
|
github.com/spf13/cast v1.3.1 // indirect
|
||||||
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/subosito/gotenv v1.2.0 // indirect
|
||||||
|
github.com/unknwon/com v1.0.1 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
|
golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
|
||||||
golang.org/x/text v0.3.6 // indirect
|
golang.org/x/text v0.3.6 // indirect
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||||
|
google.golang.org/grpc v1.38.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.26.0 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
9
main.go
9
main.go
|
@ -1,13 +1,18 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"log"
|
||||||
|
|
||||||
"gitea.com/gitea/pr-deployer/cmd"
|
"gitea.com/gitea/pr-deployer/cmd"
|
||||||
|
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
if err := settings.Init(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := cmd.Execute(); err != nil {
|
if err := cmd.Execute(); err != nil {
|
||||||
fmt.Println(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package github
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"gitea.com/gitea/pr-deployer/pkgs/proxy"
|
||||||
|
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||||
|
|
||||||
|
"github.com/google/go-github/v39/github"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetGithubClient(token *oauth2.Token) *github.Client {
|
||||||
|
ts := oauth2.StaticTokenSource(token)
|
||||||
|
var client = &http.Client{
|
||||||
|
Transport: &oauth2.Transport{
|
||||||
|
Base: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
Proxy: func(req *http.Request) (*url.URL, error) {
|
||||||
|
return proxy.Proxy()(req)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Source: oauth2.ReuseTokenSource(nil, ts),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
githubClient := github.NewClient(client)
|
||||||
|
if settings.BaseURL != "https://github.com" {
|
||||||
|
githubClient, _ = github.NewEnterpriseClient(settings.BaseURL, settings.BaseURL, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
return githubClient
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||||
|
|
||||||
|
"github.com/gobwas/glob"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
once sync.Once
|
||||||
|
hostMatchers []glob.Glob
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetProxyURL returns proxy url
|
||||||
|
func GetProxyURL() string {
|
||||||
|
if !settings.Proxy.Enabled {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Proxy.ProxyURL == "" {
|
||||||
|
if os.Getenv("http_proxy") != "" {
|
||||||
|
return os.Getenv("http_proxy")
|
||||||
|
}
|
||||||
|
return os.Getenv("https_proxy")
|
||||||
|
}
|
||||||
|
return settings.Proxy.ProxyURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match return true if url needs to be proxied
|
||||||
|
func Match(u string) bool {
|
||||||
|
if !settings.Proxy.Enabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// enforce do once
|
||||||
|
Proxy()
|
||||||
|
|
||||||
|
for _, v := range hostMatchers {
|
||||||
|
if v.Match(u) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxy returns the system proxy
|
||||||
|
func Proxy() func(req *http.Request) (*url.URL, error) {
|
||||||
|
if !settings.Proxy.Enabled {
|
||||||
|
return func(req *http.Request) (*url.URL, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if settings.Proxy.ProxyURL == "" {
|
||||||
|
return http.ProxyFromEnvironment
|
||||||
|
}
|
||||||
|
|
||||||
|
once.Do(func() {
|
||||||
|
for _, h := range settings.Proxy.ProxyHosts {
|
||||||
|
if g, err := glob.Compile(h); err == nil {
|
||||||
|
hostMatchers = append(hostMatchers, g)
|
||||||
|
} else {
|
||||||
|
log.Error("glob.Compile %s failed: %v", h, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return func(req *http.Request) (*url.URL, error) {
|
||||||
|
for _, v := range hostMatchers {
|
||||||
|
if v.Match(req.URL.Host) {
|
||||||
|
return http.ProxyURL(settings.Proxy.ProxyURLFixed)(req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.ProxyFromEnvironment(req)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
my_github "gitea.com/gitea/pr-deployer/pkgs/github"
|
||||||
|
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||||
|
|
||||||
|
"github.com/google/go-github/v39/github"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPullRequests(token *oauth2.Token, p int) ([]*github.PullRequest, error) {
|
||||||
|
c := my_github.GetGithubClient(token)
|
||||||
|
pulls, _, err := c.PullRequests.List(context.Background(), settings.RepoOwner, settings.RepoName, &github.PullRequestListOptions{
|
||||||
|
Sort: "updated",
|
||||||
|
Direction: "desc",
|
||||||
|
ListOptions: github.ListOptions{
|
||||||
|
Page: p,
|
||||||
|
PerPage: 50,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return pulls, err
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package services
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -10,13 +11,94 @@ import (
|
||||||
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||||
|
|
||||||
"github.com/cloudflare/cloudflare-go"
|
"github.com/cloudflare/cloudflare-go"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
git "github.com/go-git/go-git/v5"
|
git "github.com/go-git/go-git/v5"
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
|
"github.com/google/go-github/v39/github"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UpdateAndStartPullRequest(number int) error {
|
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
|
// 1 download the git
|
||||||
p := filepath.Join(settings.CodeCacheDir, strconv.Itoa(number))
|
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)
|
fmt.Println("clone code into", p)
|
||||||
|
|
||||||
// 1.1 git fetch origin +refs/heads/main
|
// 1.1 git fetch origin +refs/heads/main
|
||||||
|
@ -47,17 +129,26 @@ func UpdateAndStartPullRequest(number int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 2 build
|
func getImageTag(number int) string {
|
||||||
|
return fmt.Sprintf("pr-%d", number)
|
||||||
|
}
|
||||||
|
|
||||||
// 3 change domain
|
// buildImage build the docker image via Dockerfile
|
||||||
if err := checkAndUpdateSubDomain(number); err != nil {
|
func buildImage(ctx context.Context, number int) error {
|
||||||
|
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4 change reverse server
|
var buildContext io.Reader
|
||||||
|
if _, err = cli.ImageBuild(ctx, buildContext, types.ImageBuildOptions{
|
||||||
// 5 send commit status
|
Tags: []string{getImageTag(number)},
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,126 @@
|
||||||
package settings
|
package settings
|
||||||
|
|
||||||
var (
|
import (
|
||||||
Domain string
|
"errors"
|
||||||
DomainIP string
|
"fmt"
|
||||||
WebhookSecretKey []byte
|
"net/url"
|
||||||
CodeCacheDir string
|
"os"
|
||||||
CloudflareToken string
|
"strings"
|
||||||
CloudflareEmail string
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ServiceType string
|
||||||
|
RepoURL string
|
||||||
|
BaseURL string
|
||||||
|
RepoOwner string
|
||||||
|
RepoName string
|
||||||
|
OAuth2CallbackURL string
|
||||||
|
OAuth2ServerURL string
|
||||||
|
OAuth2ClientID string
|
||||||
|
OAuth2ClientSecret string
|
||||||
|
Domain string
|
||||||
|
DomainIP string
|
||||||
|
WebhookSecretKey []byte
|
||||||
|
CodeCacheDir string
|
||||||
|
CloudflareToken string
|
||||||
|
CloudflareEmail string
|
||||||
|
Proxy struct {
|
||||||
|
Enabled bool
|
||||||
|
ProxyURL string
|
||||||
|
ProxyURLFixed *url.URL
|
||||||
|
ProxyHosts []string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
OAuth2Conf *oauth2.Config
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init() error {
|
||||||
|
viper.SetConfigFile("./config.yaml")
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceType = viper.GetString("ServiceType")
|
||||||
|
if ServiceType == "" {
|
||||||
|
return errors.New("ServiceType cannot be empty")
|
||||||
|
}
|
||||||
|
if !strings.EqualFold(ServiceType, "github") && !strings.EqualFold(ServiceType, "gitea") {
|
||||||
|
return fmt.Errorf("unknow service type: %s", ServiceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
RepoURL = viper.GetString("RepoURL")
|
||||||
|
if RepoURL == "" {
|
||||||
|
return errors.New("RepoURL cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(RepoURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseURL = u.Scheme + "://" + u.Host
|
||||||
|
var p = u.Path
|
||||||
|
if strings.HasPrefix(p, "/") {
|
||||||
|
p = p[1:]
|
||||||
|
}
|
||||||
|
fields := strings.Split(p, "/")
|
||||||
|
RepoOwner = fields[0]
|
||||||
|
RepoName = fields[1]
|
||||||
|
|
||||||
|
OAuth2CallbackURL = viper.GetString("OAuth2.CallbackURL")
|
||||||
|
if OAuth2CallbackURL == "" {
|
||||||
|
return errors.New("OAuth2CallbackURL cannot be empty")
|
||||||
|
}
|
||||||
|
OAuth2ServerURL = viper.GetString("OAuth2.ServerURL")
|
||||||
|
if OAuth2ServerURL == "" {
|
||||||
|
return errors.New("OAuth2ServerURL cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2ClientID = viper.GetString("OAuth2.ClientID")
|
||||||
|
if OAuth2ClientID == "" {
|
||||||
|
return errors.New("OAuth2ClientID cannot be empty")
|
||||||
|
}
|
||||||
|
OAuth2ClientSecret = viper.GetString("OAuth2.ClientSecret")
|
||||||
|
if OAuth2ClientSecret == "" {
|
||||||
|
return errors.New("OAuth2ClientSecret cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
Domain = viper.GetString("Domain")
|
||||||
|
DomainIP = viper.GetString("DomainIP")
|
||||||
|
WebhookSecretKey = []byte(viper.GetString("WebhookSecretKey"))
|
||||||
|
CodeCacheDir = viper.GetString("CodeCacheDir")
|
||||||
|
if err := os.MkdirAll(CodeCacheDir, os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
CloudflareToken = viper.GetString("CloudflareToken")
|
||||||
|
CloudflareEmail = viper.GetString("CloudflareEmail")
|
||||||
|
|
||||||
|
OAuth2Conf = &oauth2.Config{
|
||||||
|
ClientID: OAuth2ClientID,
|
||||||
|
ClientSecret: OAuth2ClientSecret, // change this to your gitea secret id
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
TokenURL: fmt.Sprintf("%s/login/oauth/access_token", OAuth2ServerURL),
|
||||||
|
AuthURL: fmt.Sprintf("%s/login/oauth/authorize", OAuth2ServerURL),
|
||||||
|
},
|
||||||
|
RedirectURL: fmt.Sprintf("%s/callback", OAuth2CallbackURL),
|
||||||
|
}
|
||||||
|
|
||||||
|
Proxy.Enabled = viper.GetBool("Proxy.Enabled")
|
||||||
|
Proxy.ProxyURL = viper.GetString("Proxy.ProxyURL")
|
||||||
|
if Proxy.ProxyURL != "" {
|
||||||
|
var err error
|
||||||
|
Proxy.ProxyURLFixed, err = url.Parse(Proxy.ProxyURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Proxy.ProxyHosts = viper.GetStringSlice("Proxy.ProxyHosts")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.com/gitea/pr-deployer/pkgs/proxy"
|
||||||
|
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||||
|
"gitea.com/go-chi/session"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Home(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sess := session.GetSession(r)
|
||||||
|
user := sess.Get("user")
|
||||||
|
oriState := "111111" // TODO: use a random
|
||||||
|
|
||||||
|
if user == nil {
|
||||||
|
sess.Set("state", oriState)
|
||||||
|
|
||||||
|
url := settings.OAuth2Conf.AuthCodeURL(oriState, oauth2.AccessTypeOffline)
|
||||||
|
fmt.Printf("Visit the URL for the auth dialog: %v", url)
|
||||||
|
|
||||||
|
http.Redirect(w, r, url, http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/prs", http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func OAuth2Callback(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sess := session.GetSession(r)
|
||||||
|
oriState := sess.Get("state")
|
||||||
|
state := r.FormValue("state")
|
||||||
|
if state != oriState {
|
||||||
|
log.Error("state is not equal")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Timeout: 20 * time.Second,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
Proxy: func(req *http.Request) (*url.URL, error) {
|
||||||
|
return proxy.Proxy()(req)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
|
||||||
|
|
||||||
|
code := r.FormValue("code")
|
||||||
|
tok, err := settings.OAuth2Conf.Exchange(ctx, code)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(tok)
|
||||||
|
sess.Set("token", tok)
|
||||||
|
|
||||||
|
client := settings.OAuth2Conf.Client(ctx, tok)
|
||||||
|
var url string
|
||||||
|
if settings.ServiceType == "gitea" {
|
||||||
|
url = settings.OAuth2ServerURL + "/api/v1/user"
|
||||||
|
} else if settings.ServiceType == "github" {
|
||||||
|
url = "https://api.github.com/user"
|
||||||
|
}
|
||||||
|
resp, err := client.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
bs, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(bs))
|
||||||
|
|
||||||
|
var user = make(map[string]interface{})
|
||||||
|
err = json.NewDecoder(bytes.NewReader(bs)).Decode(&user)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(user)
|
||||||
|
sess.Set("user", user)
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/", 302)
|
||||||
|
}
|
|
@ -2,57 +2,68 @@ package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"gitea.com/gitea/pr-deployer/pkgs/services"
|
||||||
|
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
|
"gitea.com/go-chi/session"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
"github.com/google/go-github/v39/github"
|
"github.com/google/go-github/v39/github"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/unrolled/render"
|
"github.com/unrolled/render"
|
||||||
|
|
||||||
"gitea.com/gitea/pr-deployer/pkgs/services"
|
|
||||||
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var rnd *render.Render
|
var rnd *render.Render
|
||||||
|
|
||||||
func Web() {
|
func Web() {
|
||||||
rnd = render.New()
|
rnd = render.New(render.Options{
|
||||||
|
IsDevelopment: true,
|
||||||
|
})
|
||||||
|
|
||||||
c := chi.NewRouter()
|
c := chi.NewRouter()
|
||||||
|
c.Use(session.Sessioner())
|
||||||
c.Use(middleware.Logger)
|
c.Use(middleware.Logger)
|
||||||
c.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Write([]byte("You are not login"))
|
c.Get("/", Home)
|
||||||
})
|
c.Get("/callback", OAuth2Callback)
|
||||||
c.Get("/prs", ListPRs)
|
c.Get("/prs", ListPRs)
|
||||||
c.Post("/pr/{index}/run", RunPR)
|
c.Post("/pr/{index}/run", RunPR)
|
||||||
c.Post("/pr/{index}/stop", StopPR)
|
c.Post("/pr/{index}/stop", StopPR)
|
||||||
c.Post("/webhook", Webhook)
|
c.Post("/webhook", Webhook)
|
||||||
|
|
||||||
http.ListenAndServe(":3001", c)
|
http.ListenAndServe(":3001", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListPRs(w http.ResponseWriter, r *http.Request) {
|
func ListPRs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sess := session.GetSession(r)
|
||||||
|
pToken := sess.Get("token")
|
||||||
|
if pToken == nil {
|
||||||
|
http.Redirect(w, r, "/", 302)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
p, _ := strconv.Atoi(r.FormValue("p"))
|
p, _ := strconv.Atoi(r.FormValue("p"))
|
||||||
if p < 1 {
|
if p < 1 {
|
||||||
p = 1
|
p = 1
|
||||||
}
|
}
|
||||||
c := github.NewClient(http.DefaultClient)
|
|
||||||
pulls, _, err := c.PullRequests.List(context.Background(), "go-gitea", "gitea", &github.PullRequestListOptions{
|
token := pToken.(*oauth2.Token)
|
||||||
Sort: "updated",
|
pulls, err := services.GetPullRequests(token, p)
|
||||||
Direction: "desc",
|
|
||||||
ListOptions: github.ListOptions{
|
|
||||||
Page: p,
|
|
||||||
PerPage: 100,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user := sess.Get("user")
|
||||||
|
|
||||||
if err := rnd.HTML(w, http.StatusOK, "pulls", map[string]interface{}{
|
if err := rnd.HTML(w, http.StatusOK, "pulls", map[string]interface{}{
|
||||||
"pulls": pulls,
|
"pulls": pulls,
|
||||||
|
"user": user,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -65,7 +76,8 @@ func RunPR(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Error("start failed")
|
log.Error("start failed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := services.UpdateAndStartPullRequest(i); err != nil {
|
ctx := context.Background()
|
||||||
|
if err := services.UpdateAndStartPullRequest(ctx, i, fmt.Sprintf("refs/pull/%d/head", i)); err != nil {
|
||||||
log.Error("start failed")
|
log.Error("start failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,9 +108,16 @@ func Webhook(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
switch evt := event.(type) {
|
switch evt := event.(type) {
|
||||||
case *github.PullRequestEvent:
|
case *github.PullRequestEvent:
|
||||||
if err := services.UpdateAndStartPullRequest(*evt.Number); err != nil {
|
switch *evt.Action {
|
||||||
log.Error(err)
|
case "synchronize":
|
||||||
|
ctx := context.Background()
|
||||||
|
if err := services.UpdateAndStartPullRequest(ctx, *evt.Number, *evt.After); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Trace("pull request %d is %s", *evt.Number, *evt.Action)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Warn("received %v type event, ignored", event)
|
log.Warn("received %v type event, ignored", event)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<div>{{.User.Name}}</div>
|
|
@ -1,5 +1,6 @@
|
||||||
|
<div>{{.user.name}}</div>
|
||||||
<ul>
|
<ul>
|
||||||
{{range .pulls}}
|
{{range .pulls}}
|
||||||
<li>{{.Number}}</li>
|
<li>{{.Number}}: <a href="{{.HTMLURL}}">{{.Title}}</a> <button>Start Service</button></li>
|
||||||
{{end
|
{{end}}
|
||||||
</ul>
|
</ul>
|
Loading…
Reference in New Issue