Patcher/apply.go
2024-12-08 13:41:19 +01:00

191 lines
4.7 KiB
Go
Executable file

package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
)
func main() {
// Define the --path flag
rootPath := flag.String("path", ".", "Root path for patch application")
flag.Parse()
// Convert root path to an absolute path
absoluteRootPath, err := filepath.Abs(*rootPath)
if err != nil {
log.Fatalf("Failed to resolve absolute path for root: %v", err)
}
fmt.Printf("Starting custom patcher...\nRoot path: %s\n", absoluteRootPath)
patchDir := "./patches"
var successfulPatches, failedPatches []string
err = filepath.Walk(patchDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(info.Name(), ".patch") {
fmt.Printf("Applying patch: %s\n", path)
err := applyPatch(path, absoluteRootPath)
if err != nil {
fmt.Printf("Failed to apply patch %s: %v\n", path, err)
failedPatches = append(failedPatches, path)
} else {
fmt.Printf("Successfully applied patch: %s\n", path)
successfulPatches = append(successfulPatches, path)
}
}
return nil
})
if err != nil {
log.Fatalf("Error reading patches: %v", err)
}
// Print the summary
fmt.Println("\nSummary:")
fmt.Printf("Successful patches (%d):\n", len(successfulPatches))
for _, patch := range successfulPatches {
fmt.Printf(" - %s\n", patch)
}
fmt.Printf("Failed patches (%d):\n", len(failedPatches))
for _, patch := range failedPatches {
fmt.Printf(" - %s\n", patch)
}
// Exit with appropriate status for Forgejo/Gitea runners
if len(failedPatches) > 0 {
fmt.Println("\nPatch process completed with failures.")
os.Exit(1)
} else {
fmt.Println("\nAll patches applied successfully.")
os.Exit(0)
}
}
// applyPatch processes a single patch file
func applyPatch(patchPath, rootPath string) error {
// Open the patch file
file, err := os.Open(patchPath)
if err != nil {
return fmt.Errorf("failed to open patch file: %v", err)
}
defer file.Close()
var targetFilePath, tempFilePath string
var modifications []string
scanner := bufio.NewScanner(file)
isReadingChanges := false
// Parse the patch file
for scanner.Scan() {
line := scanner.Text()
// Detect the target file
if strings.HasPrefix(line, "--- ") {
relativePath := strings.TrimSpace(strings.TrimPrefix(line, "--- "))
targetFilePath = filepath.Join(rootPath, relativePath)
isReadingChanges = true
continue
}
// Start processing modifications
if isReadingChanges {
modifications = append(modifications, line)
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to read patch file: %v", err)
}
// Open the target file for reading
targetFile, err := os.Open(targetFilePath)
if err != nil {
return fmt.Errorf("failed to open target file: %v", err)
}
defer targetFile.Close()
// Prepare a temporary file for output
tempFilePath = targetFilePath + ".tmp"
tempFile, err := os.Create(tempFilePath)
if err != nil {
return fmt.Errorf("failed to create temp file: %v", err)
}
defer tempFile.Close()
scanner = bufio.NewScanner(targetFile)
var targetLines []string
for scanner.Scan() {
targetLines = append(targetLines, scanner.Text())
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to read target file: %v", err)
}
// Apply the modifications
updatedLines, err := applyModifications(targetLines, modifications)
if err != nil {
return err
}
// Write the updated lines to the temporary file
for _, line := range updatedLines {
_, err := fmt.Fprintln(tempFile, line)
if err != nil {
return fmt.Errorf("failed to write to temp file: %v", err)
}
}
// Replace the original file with the updated file
err = os.Rename(tempFilePath, targetFilePath)
if err != nil {
return fmt.Errorf("failed to replace target file: %v", err)
}
return nil
}
// applyModifications applies the changes from the patch file to the target lines
func applyModifications(targetLines, modifications []string) ([]string, error) {
var result []string
i := 0
for _, mod := range modifications {
switch {
case strings.HasPrefix(mod, "-"): // Delete or replace
mod = strings.TrimPrefix(mod, "-")
for i < len(targetLines) {
if strings.TrimSpace(targetLines[i]) == strings.TrimSpace(mod) {
i++ // Skip this line (delete)
break
}
result = append(result, targetLines[i])
i++
}
case strings.HasPrefix(mod, "+"): // Add or replace
mod = strings.TrimPrefix(mod, "+")
result = append(result, mod)
default: // Keep existing lines
if i < len(targetLines) {
result = append(result, targetLines[i])
i++
}
}
}
// Append remaining lines from the original file
for i < len(targetLines) {
result = append(result, targetLines[i])
i++
}
return result, nil
}