feat: add Forgejo webhook trigger support with AWS Secrets Manager
This commit is contained in:
parent
0819ae1a71
commit
bba136cb12
5 changed files with 238 additions and 11 deletions
74
app/main.go
74
app/main.go
|
|
@ -2,6 +2,9 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
|
@ -24,16 +27,67 @@ type Config struct {
|
|||
AWSRegion string
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
StatusCode int `json:"statusCode"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
var commandRunner = exec.Command
|
||||
|
||||
func handleRequest(ctx context.Context, event json.RawMessage) error {
|
||||
// verifySignature computes an HMAC using the provided secret and compares it to the incoming signature.
|
||||
func verifySignature(secret, body, signatureHeader string) bool {
|
||||
// Assuming the header is in the format "sha256=<signature>"
|
||||
const prefix = "sha256="
|
||||
if len(signatureHeader) < len(prefix) || signatureHeader[:len(prefix)] != prefix {
|
||||
return false
|
||||
}
|
||||
receivedSig := signatureHeader[len(prefix):]
|
||||
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
mac.Write([]byte(body))
|
||||
expectedSig := hex.EncodeToString(mac.Sum(nil))
|
||||
return hmac.Equal([]byte(receivedSig), []byte(expectedSig))
|
||||
}
|
||||
|
||||
func handleRequest(ctx context.Context, event json.RawMessage) (Response, error) {
|
||||
// For demonstration, assume the event JSON includes a "body" and "headers" map.
|
||||
var req struct {
|
||||
Body string `json:"body"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
}
|
||||
if err := json.Unmarshal(event, &req); err != nil {
|
||||
log.Printf("Error unmarshalling event: %v", err)
|
||||
return Response{StatusCode: 400, Headers: map[string]string{"Content-Type": "application/json"}, Body: "{\"message\":\"Bad Request\"}"}, err
|
||||
}
|
||||
|
||||
secret := os.Getenv("WEBHOOK_SECRET")
|
||||
if secret == "" {
|
||||
log.Println("WEBHOOK_SECRET is not set")
|
||||
return Response{StatusCode: 500, Headers: map[string]string{"Content-Type": "application/json"}, Body: "{\"message\":\"Server configuration error\"}"}, fmt.Errorf("WEBHOOK_SECRET is not set")
|
||||
}
|
||||
|
||||
signature := req.Headers["X-Hub-Signature-256"] // adjust this header name as appropriate.
|
||||
if signature == "" || !verifySignature(secret, req.Body, signature) {
|
||||
log.Println("Signature verification failed")
|
||||
return Response{StatusCode: 401, Headers: map[string]string{"Content-Type": "application/json"}, Body: "{\"message\":\"Unauthorized\"}"}, fmt.Errorf("signature verification failed")
|
||||
}
|
||||
|
||||
// Call your existing process (for example, runDeploymentProcess)
|
||||
if err := runDeploymentProcess(ctx); err != nil {
|
||||
// Log the error; you may also report it via CloudWatch alarms
|
||||
log.Printf("Deployment process failed: %v", err)
|
||||
return err
|
||||
return Response{
|
||||
StatusCode: 500,
|
||||
Headers: map[string]string{"Content-Type": "application/json"},
|
||||
Body: fmt.Sprintf("{\"message\": \"Deployment process failed: %v\"}", err),
|
||||
}, err
|
||||
}
|
||||
return nil
|
||||
return Response{
|
||||
StatusCode: 200,
|
||||
Headers: map[string]string{"Content-Type": "application/json"},
|
||||
Body: "{\"message\": \"Success\"}",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
@ -44,14 +98,14 @@ func runDeploymentProcess(ctx context.Context) error {
|
|||
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("Configuration error: %v", err)
|
||||
log.Printf("Configuration error: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a unique temp directory for this run
|
||||
repoDir, err := os.MkdirTemp("", "repo-*")
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating temporary directory: %v", err)
|
||||
log.Printf("Error creating temporary directory: %v", err)
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(repoDir)
|
||||
|
|
@ -59,26 +113,26 @@ func runDeploymentProcess(ctx context.Context) error {
|
|||
|
||||
// 1. Clone the repository
|
||||
if err := cloneRepository(ctx, cfg.RepoURL, cfg.RepoBranch, repoDir); err != nil {
|
||||
log.Fatalf("Failure in cloning: %v", err)
|
||||
log.Printf("Failure in cloning: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. Create a ZIP archive of the repository
|
||||
if err := createZipArchive(ctx, repoDir, zipFilePath); err != nil {
|
||||
log.Fatalf("Failure in creating ZIP archive: %v", err)
|
||||
log.Printf("Failure in creating ZIP archive: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. Upload the ZIP file to S3
|
||||
cfg_s3, err := config.LoadDefaultConfig(ctx, config.WithRegion(cfg.AWSRegion))
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading configuration: %v", err)
|
||||
log.Printf("Error loading configuration: %v", err)
|
||||
return err
|
||||
}
|
||||
s3Client := s3.NewFromConfig(cfg_s3)
|
||||
uploader := manager.NewUploader(s3Client)
|
||||
if err := uploadToS3WithUploader(ctx, zipFilePath, cfg.S3Bucket, cfg.S3Key, uploader); err != nil {
|
||||
log.Fatalf("Failure in uploading to S3: %v", err)
|
||||
log.Printf("Failure in uploading to S3: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +203,7 @@ func uploadToS3WithUploader(ctx context.Context, zipPath, bucket, key string, up
|
|||
// Open the ZIP file
|
||||
f, err := os.Open(zipPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Error opening ZIP file: %v", err)
|
||||
return fmt.Errorf("Error opening ZIP file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue