github.com/twelho/conform@v0.0.0-20231016230407-c25e9238598a/cmd/conform/serve.go (about)

     1  // This Source Code Form is subject to the terms of the Mozilla Public
     2  // License, v. 2.0. If a copy of the MPL was not distributed with this
     3  // file, You can obtain one at http://mozilla.org/MPL/2.0/.
     4  
     5  package main
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"log"
    12  	"net/http"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  
    17  	git "github.com/go-git/go-git/v5"
    18  	"github.com/go-git/go-git/v5/config"
    19  	"github.com/go-git/go-git/v5/plumbing"
    20  	"github.com/google/go-github/v41/github"
    21  	"github.com/spf13/cobra"
    22  )
    23  
    24  const (
    25  	path = "/github"
    26  )
    27  
    28  // serveCmd represents the serve command.
    29  var serveCmd = &cobra.Command{
    30  	Use:   "serve",
    31  	Short: "",
    32  	Long:  ``,
    33  	Run: func(cmd *cobra.Command, args []string) {
    34  		if err := os.MkdirAll("/tmp", 0o700); err != nil {
    35  			log.Fatal(err)
    36  		}
    37  
    38  		http.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
    39  			payload, err := io.ReadAll(r.Body)
    40  			if err != nil {
    41  				log.Printf("failed to read payload: %+v\n", err)
    42  
    43  				return
    44  			}
    45  
    46  			go func() {
    47  				dir, err := os.CreateTemp("/tmp", "conform")
    48  				if err != nil {
    49  					log.Printf("failed to create temporary directory: %+v\n", err)
    50  
    51  					return
    52  				}
    53  
    54  				defer os.RemoveAll(dir.Name()) //nolint:errcheck
    55  
    56  				if err = os.MkdirAll(filepath.Join(dir.Name(), "github"), 0o700); err != nil {
    57  					log.Printf("failed to create github directory: %+v\n", err)
    58  
    59  					return
    60  				}
    61  				if err = os.MkdirAll(filepath.Join(dir.Name(), "repo"), 0o700); err != nil {
    62  					log.Printf("failed to create repo directory: %+v\n", err)
    63  
    64  					return
    65  				}
    66  
    67  				event := filepath.Join(dir.Name(), "github", "event.json")
    68  				pullRequestEvent := &github.PullRequestEvent{}
    69  				if err = json.Unmarshal(payload, pullRequestEvent); err != nil {
    70  					log.Printf("failed to parse pull_request event: %+v\n", err)
    71  
    72  					return
    73  				}
    74  
    75  				cloneRepo := filepath.Join(dir.Name(), "repo")
    76  				cloneURL := pullRequestEvent.GetPullRequest().GetBase().GetRepo().GetCloneURL()
    77  
    78  				log.Printf("Cloning %s", cloneURL)
    79  
    80  				repo, err := git.PlainClone(cloneRepo, false, &git.CloneOptions{
    81  					SingleBranch: false,
    82  					NoCheckout:   true,
    83  					URL:          cloneURL,
    84  					Progress:     os.Stdout,
    85  				})
    86  				if err != nil {
    87  					log.Printf("failed to clone repo: %+v\n", err)
    88  
    89  					return
    90  				}
    91  
    92  				id := pullRequestEvent.GetPullRequest().GetNumber()
    93  
    94  				ref := plumbing.ReferenceName(pullRequestEvent.GetPullRequest().GetHead().GetRef())
    95  
    96  				refSpec := fmt.Sprintf("refs/pull/%d/head:%s", id, ref)
    97  
    98  				err = repo.Fetch(&git.FetchOptions{
    99  					RefSpecs: []config.RefSpec{
   100  						config.RefSpec("refs/heads/*:refs/heads/*"),
   101  						config.RefSpec(refSpec),
   102  					},
   103  					Progress: os.Stdout,
   104  				})
   105  				if err != nil {
   106  					log.Printf("failed to fetch %q: %v", refSpec, err)
   107  
   108  					return
   109  				}
   110  
   111  				worktree, err := repo.Worktree()
   112  				if err != nil {
   113  					log.Printf("failed to get working tree: %v", err)
   114  
   115  					return
   116  				}
   117  
   118  				err = worktree.Checkout(&git.CheckoutOptions{
   119  					Branch: ref,
   120  				})
   121  
   122  				if err != nil {
   123  					log.Printf("failed to checkout %q: %v", ref, err)
   124  
   125  					return
   126  				}
   127  
   128  				log.Printf("writing %s to disk", event)
   129  
   130  				if err = os.WriteFile(event, payload, 0o600); err != nil {
   131  					log.Printf("failed to write event to disk: %+v\n", err)
   132  
   133  					return
   134  				}
   135  				cmd := exec.Command("/proc/self/exe", "enforce", "--reporter=github", "--commit-ref=refs/heads/"+pullRequestEvent.GetPullRequest().GetBase().GetRef())
   136  				cmd.Stdout = os.Stdout
   137  				cmd.Stderr = os.Stdout
   138  				cmd.Dir = cloneRepo
   139  				cmd.Env = []string{fmt.Sprintf("INPUT_TOKEN=%s", os.Getenv("INPUT_TOKEN")), fmt.Sprintf("GITHUB_EVENT_PATH=%s", event)}
   140  				err = cmd.Start()
   141  				if err != nil {
   142  					log.Printf("failed to start command: %+v\n", err)
   143  
   144  					return
   145  				}
   146  				err = cmd.Wait()
   147  				if err != nil {
   148  					log.Printf("command failed: %+v\n", err)
   149  
   150  					return
   151  				}
   152  			}()
   153  
   154  			w.WriteHeader(http.StatusOK)
   155  		})
   156  
   157  		log.Fatal(http.ListenAndServe(":3000", nil))
   158  	},
   159  }
   160  
   161  func init() {
   162  	rootCmd.AddCommand(serveCmd)
   163  }