191 lines
4.7 KiB
Go
Executable file
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
|
|
}
|