package main import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "os" "os/exec" "os/signal" "path" "runtime" "strings" "syscall" "gitea.urkob.com/urko/gitea-webhook-listener/internal" "gitea.urkob.com/urko/gitea-webhook-listener/kit/config" ) func main() { var err error ctx, cancel := context.WithCancel(signalContext(context.Background())) defer cancel() cfgFile := os.Getenv("CONFIG_FILE") if cfgFile == "" { // Get root path _, filename, _, _ := runtime.Caller(0) cfgFile = path.Join(path.Dir(filename), "configs", "app.yml") } cfg, err := config.LoadConfig(cfgFile) if err != nil { log.Fatalf("Error loading config: %v", err) } http.HandleFunc("/", handlePayload(cfg.Secret, cfg.Projects)) go func() { err = http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), nil) }() log.Printf("server is up on %d\n", cfg.Port) <-ctx.Done() if err != nil { log.Println("some error happened", err) } log.Println("server shutdown") } func handlePayload(secret string, projects map[string][]config.ConfigScript) func(w http.ResponseWriter, r *http.Request) { return (func(w http.ResponseWriter, r *http.Request) { // Read the request body body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "Failed to read request body", http.StatusBadRequest) return } defer r.Body.Close() authHeader := r.Header.Get("Authorization") if authHeader != secret { http.Error(w, "Signatures didn't match", http.StatusUnauthorized) return } if !r.URL.Query().Has("project") { http.Error(w, "", http.StatusBadRequest) return } project := r.URL.Query().Get("project") proj, found := projects[project] if !found { http.Error(w, "not found", http.StatusNotFound) log.Printf("project %s not found\n", project) return } var payload internal.WebhookPayload if err = json.Unmarshal(body, &payload); err != nil { http.Error(w, "Failed to parse JSON payload", http.StatusBadRequest) return } branchName := strings.Split(payload.Ref, "/")[len(strings.Split(payload.Ref, "/"))-1] log.Println("branchName", branchName) found = false var scr config.ConfigScript for i := range proj { if proj[i].Environment == branchName { found = true scr = proj[i] break } } if !found { http.Error(w, "not found", http.StatusNotFound) log.Printf("project %s with branch %s not found\n", project, branchName) return } go func() { if err := execute(scr.Command, scr.Arguments...); err != nil { log.Println(err) } log.Println("script was deployed successful") }() }) } func execute(command string, args ...string) error { cmd := exec.Command(command, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return fmt.Errorf("cmd.Run %w", err) } log.Println() return nil } func signalContext(ctx context.Context) context.Context { ctx, cancel := context.WithCancel(ctx) sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigs signal.Stop(sigs) close(sigs) cancel() }() return ctx }