changed to golang

This commit is contained in:
partisan 2024-09-09 01:25:07 +02:00
parent f904f731f3
commit 89687eecd6
12 changed files with 867 additions and 484 deletions

181
spitfire/appindex.go Normal file
View file

@ -0,0 +1,181 @@
package spitfire
import (
"crypto/sha1"
"fmt"
"io"
"log"
"os"
"strings"
"time"
)
// Package the APPINDEX update process
func PackageAPPINDEX(name, release, version, arch, size, installedSize, description, url, license, origin, maintainer, dependencies, platform string) error {
// Mock package file name
pkgFile := fmt.Sprintf("%s-%s", name, version)
// Calculate checksums
checksum := calcChecksum(pkgFile)
contentChecksum := calcChecksum(pkgFile)
// Timestamp
timestamp := time.Now().Unix()
// Remove existing entry based on P, R, A, and o fields
removeExistingEntry(name, release, arch, origin)
// Open or create the APPINDEX file for appending
file, err := os.OpenFile("./APPINDEX", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("Failed to open APPINDEX file: %v", err)
}
defer file.Close()
// Write the new entry
entry := fmt.Sprintf(`
C:%s
P:%s
R:%s
V:%s
A:%s
S:%s
I:%s
T:%s
U:%s
L:%s
o:%s
m:%s
t:%d
c:%s
D:%s
p:%s
q:
Z:%s
`, checksum, name, release, version, arch, size, installedSize, description, url, license, origin, maintainer, timestamp, contentChecksum, dependencies, platform, checksum)
if _, err := file.WriteString(entry); err != nil {
log.Fatalf("Failed to write to APPINDEX file: %v", err)
}
fmt.Println("APPINDEX has been updated successfully.")
return nil
}
// calcChecksum calculates a checksum (SHA-1) for a given input string
func calcChecksum(input string) string {
h := sha1.New()
_, _ = io.WriteString(h, input)
return fmt.Sprintf("%x", h.Sum(nil))
}
// removeExistingEntry removes an existing entry from APPINDEX based on P, R, A, and o fields
func removeExistingEntry(name, release, arch, origin string) {
// Read file contents
content, err := os.ReadFile("./APPINDEX")
if err != nil {
if os.IsNotExist(err) {
return // If file does not exist, no need to remove anything
}
log.Fatalf("Failed to read APPINDEX: %v", err)
}
// Remove lines matching the package with the same P, R, A, and o fields
lines := strings.Split(string(content), "\n")
var newLines []string
remove := false
for _, line := range lines {
// Detect start of an entry by matching on P, R, A, and o
if strings.HasPrefix(line, "P:"+name) {
remove = true
}
if remove && strings.HasPrefix(line, "R:"+release) {
remove = true
}
if remove && strings.HasPrefix(line, "A:"+arch) {
remove = true
}
if remove && strings.HasPrefix(line, "o:"+origin) {
remove = true
}
// Stop removal at the end of an entry
if remove && line == "" {
remove = false
continue // Skip the line
}
// Append lines that are not part of the matching entry
if !remove {
newLines = append(newLines, line)
}
}
// Write the updated contents back to the file
err = os.WriteFile("./APPINDEX", []byte(strings.Join(newLines, "\n")), 0644)
if err != nil {
log.Fatalf("Failed to update APPINDEX: %v", err)
}
}
// CleanAppIndex cleans up any orphaned "C:" entries and collapses excessive newlines
func CleanAppIndex() error {
// Read file contents
content, err := os.ReadFile("./APPINDEX")
if err != nil {
return fmt.Errorf("failed to read APPINDEX: %v", err)
}
// Split the file content into lines
lines := strings.Split(string(content), "\n")
var newLines []string
var currentEntry []string
inEntry := false
for _, line := range lines {
line = strings.TrimSpace(line)
// Start of an entry when we encounter a checksum
if strings.HasPrefix(line, "C:") {
// If we already have a valid entry, add it to newLines
if inEntry && len(currentEntry) > 1 {
newLines = append(newLines, currentEntry...)
}
currentEntry = []string{line}
inEntry = true
} else if inEntry && line == "" {
// End of an entry
if len(currentEntry) > 1 {
newLines = append(newLines, currentEntry...)
newLines = append(newLines, "") // Add a blank line to separate entries
}
currentEntry = nil
inEntry = false
} else if inEntry {
// Continue adding lines to the current entry
currentEntry = append(currentEntry, line)
} else if line != "" {
// Add non-entry lines (for extra safety)
newLines = append(newLines, line)
}
}
// In case the last entry was valid
if inEntry && len(currentEntry) > 1 {
newLines = append(newLines, currentEntry...)
}
// Collapse consecutive blank lines
cleanedContent := strings.Join(newLines, "\n")
cleanedContent = strings.ReplaceAll(cleanedContent, "\n\n\n", "\n\n")
// Write the cleaned content back to the file
err = os.WriteFile("./APPINDEX", []byte(cleanedContent), 0644)
if err != nil {
return fmt.Errorf("failed to write cleaned APPINDEX: %v", err)
}
fmt.Println("APPINDEX cleaned successfully.")
return nil
}

166
spitfire/build.go Normal file
View file

@ -0,0 +1,166 @@
package spitfire
import (
"fmt"
"os"
"os/exec"
"path/filepath"
)
// Array to store errors
var errors []string
// Run an external command like scp or rsync
func runCommand(command string, args ...string) error {
cmd := exec.Command(command, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// Function to resolve paths using absolute path
func ResolvePath(path string) (string, error) {
absPath, err := filepath.Abs(path)
if err != nil {
return "", fmt.Errorf("failed to resolve path: %s", path)
}
return absPath, nil
}
// Function to download Mozilla source if not present
func DownloadSource(sourcePath string, sourceRepo string) {
if _, err := os.Stat(sourcePath); os.IsNotExist(err) {
fmt.Println("Mozilla source not found. Cloning from repository...")
if err := runCommand("hg", "clone", sourceRepo, sourcePath); err != nil {
errors = append(errors, "Failed to clone Mozilla repository.")
}
} else {
fmt.Println("Mozilla source already exists.")
}
}
// Function to discard uncommitted changes
func DiscardChanges(sourcePath string) {
fmt.Println("Discarding uncommitted changes...")
if err := runCommand("hg", "revert", "--all", "--no-backup", "-R", sourcePath); err != nil {
errors = append(errors, "Failed to revert changes in Mozilla repository.")
}
}
// Function to clean build
func CleanBuild(sourcePath string) {
fmt.Println("Cleaning build...")
if err := os.Chdir(sourcePath); err != nil {
errors = append(errors, "Failed to navigate to source directory.")
return
}
if err := runCommand("hg", "revert", "--all", "--no-backup"); err != nil {
errors = append(errors, "Failed to revert changes in Mozilla repository.")
}
if err := runCommand("./mach", "clobber"); err != nil {
errors = append(errors, "Failed to clean build.")
}
}
// Function to update Mozilla repository
func UpdateRepo(sourcePath string) {
fmt.Println("Updating Mozilla repository...")
if err := os.Chdir(sourcePath); err != nil {
errors = append(errors, "Failed to navigate to source directory.")
return
}
if err := runCommand("hg", "pull", "-u"); err != nil {
errors = append(errors, "Failed to update Mozilla repository.")
}
}
// Function to update patches
func UpdatePatches(patchesDir, patchesRepo, sourcePath string) {
fmt.Println("Updating patches...")
if _, err := os.Stat(patchesDir); err == nil {
fmt.Println("Patches directory already exists. Cleaning and pulling updates...")
if err := os.Chdir(patchesDir); err != nil {
errors = append(errors, "Failed to navigate to patches directory.")
return
}
if err := runCommand("git", "clean", "-xdf"); err != nil {
errors = append(errors, "Failed to clean patches directory.")
}
_ = runCommand("git", "stash", "push", "--include-untracked")
if err := runCommand("git", "fetch"); err != nil {
errors = append(errors, "Failed to fetch updates from patches repository.")
}
if runCommand("git", "show-ref", "--verify", "--quiet", "refs/heads/main") == nil {
if err := runCommand("git", "rebase", "origin/main"); err != nil {
errors = append(errors, "Failed to rebase updates from main branch.")
}
} else if runCommand("git", "show-ref", "--verify", "--quiet", "refs/heads/master") == nil {
if err := runCommand("git", "rebase", "origin/master"); err != nil {
errors = append(errors, "Failed to rebase updates from master branch.")
}
} else {
errors = append(errors, "No valid branch (main or master) found in patches repository.")
return
}
if runCommand("git", "stash", "list") == nil {
_ = runCommand("git", "stash", "pop")
} else {
fmt.Println("No stash entries found, skipping pop.")
}
} else {
fmt.Println("Patches directory does not exist. Cloning repository...")
if err := runCommand("git", "clone", patchesRepo, patchesDir); err != nil {
errors = append(errors, "Failed to clone patches repository.")
}
}
fmt.Println("Copying files from patches directory to Firefox source directory...")
if err := runCommand("rsync", "-av", "--exclude=.git", patchesDir+"/", sourcePath+"/"); err != nil {
errors = append(errors, "Failed to copy files.")
}
}
// Function to configure Spitfire
func Configure(sourcePath string) {
fmt.Println("Configuring Spitfire...")
if err := os.Chdir(sourcePath); err != nil {
errors = append(errors, "Failed to navigate to source directory.")
return
}
if err := runCommand("./mach", "configure"); err != nil {
errors = append(errors, "Configuration failed.")
}
}
// Function to build Spitfire
func Build(sourcePath string) {
fmt.Println("Building Spitfire...")
if err := os.Chdir(sourcePath); err != nil {
errors = append(errors, "Failed to navigate to source directory.")
return
}
if err := runCommand("./mach", "build"); err != nil {
errors = append(errors, "Build failed.")
}
}
// Function to run the project after build
func RunProject(sourcePath string) {
fmt.Println("Running the project...")
if err := os.Chdir(sourcePath); err != nil {
errors = append(errors, "Failed to navigate to source directory.")
return
}
if err := runCommand("./mach", "run"); err != nil {
errors = append(errors, "Failed to run the project.")
}
}
// Function to print collected errors
func PrintErrors() {
if len(errors) > 0 {
fmt.Println("The following errors occurred during execution:")
for _, err := range errors {
fmt.Printf("- %s\n", err)
}
}
}

246
spitfire/upload.go Normal file
View file

@ -0,0 +1,246 @@
package spitfire
import (
"archive/tar"
"crypto/rand"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"encoding/hex"
)
// Config struct to hold SourceForge configurations
type Config struct {
SFKeyPath string
SFUser string
SFHost string
SFProject string
}
// Load the SourceForge configuration from a file
func LoadConfig() (*Config, error) {
file, err := os.Open("sourceforge_config.json") // Assuming a JSON config file
if err != nil {
return nil, err
}
defer file.Close()
config := &Config{}
if err := json.NewDecoder(file).Decode(config); err != nil {
return nil, err
}
return config, nil
}
// CompressDirectory compresses the build directory to a tar.gz file using PAX format for large file support
func CompressDirectory(srcDir, dstFile string) error {
// Create the destination file
f, err := os.Create(dstFile)
if err != nil {
return fmt.Errorf("could not create file %s: %v", dstFile, err)
}
defer f.Close()
// Create a new gzip writer
gw := gzip.NewWriter(f)
defer gw.Close()
// Create a new tar writer with PAX format for large file support
tw := tar.NewWriter(gw)
defer tw.Close()
// Walk through the source directory and add files to the tar archive
err = filepath.Walk(srcDir, func(file string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
// Create tar header using PAX format
header, err := tar.FileInfoHeader(fi, "")
if err != nil {
return err
}
// Set the correct header name, preserving the relative directory structure
relPath, err := filepath.Rel(srcDir, file)
if err != nil {
return err
}
header.Name = relPath
// Explicitly set the type flag for directories
if fi.IsDir() {
header.Typeflag = tar.TypeDir
} else if fi.Mode()&os.ModeSymlink != 0 {
// Handle symlinks
linkTarget, err := os.Readlink(file)
if err != nil {
return err
}
header.Linkname = linkTarget
}
// Write the header to the tarball
if err := tw.WriteHeader(header); err != nil {
return err
}
// If it's a directory or symlink, skip writing its contents
if fi.IsDir() || fi.Mode()&os.ModeSymlink != 0 {
return nil
}
// Open the file for reading
f, err := os.Open(file)
if err != nil {
return err
}
defer f.Close()
// Copy the file content to the tar writer
if _, err := io.Copy(tw, f); err != nil {
return err
}
return nil
})
if err != nil {
return fmt.Errorf("error walking the source directory %s: %v", srcDir, err)
}
return nil
}
// Upload the file to SourceForge, ensuring the local directory structure is created and uploaded
func Upload(config *Config, buildPath, remoteDir string) error {
// Generate a random hash for the temp directory name
randomHash, err := generateRandomHash(8) // 8 bytes = 16 hex characters
if err != nil {
return fmt.Errorf("failed to generate random hash: %v", err)
}
// Create a temporary directory with the random hash appended
tmpDir, err := os.MkdirTemp("", "spitfire-upload-"+randomHash)
if err != nil {
return fmt.Errorf("failed to create temporary directory: %v", err)
}
// Create the required local directory structure inside the temporary directory
localDir := filepath.Join(tmpDir, remoteDir)
err = os.MkdirAll(localDir, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create local directory structure: %v", err)
}
// Move the build file to the local directory structure
destinationFile := filepath.Join(localDir, filepath.Base(buildPath))
err = copyFile(buildPath, destinationFile)
if err != nil {
return fmt.Errorf("failed to copy file to local directory structure: %v", err)
}
// Upload the entire local directory structure to the remote directory
fmt.Printf("Uploading file %s to %s on SourceForge...\n", buildPath, remoteDir)
scpCmd := exec.Command("scp", "-i", config.SFKeyPath, "-r", tmpDir+"/.", fmt.Sprintf("%s@%s:%s", config.SFUser, config.SFHost, "/"))
scpCmd.Stdout = os.Stdout
scpCmd.Stderr = os.Stderr
return scpCmd.Run()
}
// Helper function to generate a random hash
func generateRandomHash(length int) (string, error) {
bytes := make([]byte, length)
_, err := rand.Read(bytes)
if err != nil {
return "", err
}
return hex.EncodeToString(bytes), nil
}
// Helper function to copy a file from src to dst
func copyFile(src, dst string) error {
sourceFile, err := os.Open(src)
if err != nil {
return err
}
defer sourceFile.Close()
destFile, err := os.Create(dst)
if err != nil {
return err
}
defer destFile.Close()
_, err = io.Copy(destFile, sourceFile)
if err != nil {
return err
}
return destFile.Sync() // Ensure all writes to the file are flushed
}
// Download the APPINDEX file from SourceForge
func DownloadAPPINDEX(config *Config, remoteDir string) error {
fmt.Println("Downloading APPINDEX from SourceForge...")
// Construct the correct path without double slashes
remoteAPPINDEXPath := filepath.Join(remoteDir, "APPINDEX")
// Run the SCP command to download the APPINDEX file
cmd := exec.Command("scp", "-i", config.SFKeyPath, fmt.Sprintf("%s@%s:%s", config.SFUser, config.SFHost, remoteAPPINDEXPath), "./APPINDEX")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
// Check if the error is due to the file not existing
if strings.Contains(err.Error(), "No such file or directory") {
fmt.Println("APPINDEX file not found on the server. A new one will be created.")
return nil // Continue without failing if the APPINDEX is missing
}
return fmt.Errorf("failed to download APPINDEX: %v", err) // Fail for other types of errors
}
fmt.Println("APPINDEX downloaded successfully.")
return nil
}
// Upload the updated APPINDEX file to SourceForge
func UploadAPPINDEX(config *Config, remoteDir string) error {
fmt.Println("Uploading updated APPINDEX to SourceForge...")
cmd := exec.Command("scp", "-i", config.SFKeyPath, "./APPINDEX", fmt.Sprintf("%s@%s:%s", config.SFUser, config.SFHost, remoteDir))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// GetDirectorySize calculates the total size of all files in a directory
func GetDirectorySize(path string) (int64, error) {
var size int64
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
size += info.Size()
}
return nil
})
return size, err
}
// GetFileSize returns the size of a file in bytes
func GetFileSize(filePath string) (int64, error) {
fileInfo, err := os.Stat(filePath)
if err != nil {
return 0, err
}
return fileInfo.Size(), nil
}