diff --git a/README.md b/README.md index 54b3411..e546e0a 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,6 @@ Below is a detailed description of all the flags supported by the Spitfire Build ### **Customization Flags** -- **`-p=`**: - Specifies the build directory path for uploads. If not provided, the script dynamically detects the build directory. - **`-t=`**: Sets the target format for the build output. The format follows `component-arch-release-platform`. - **`-v=`**: diff --git a/main.go b/main.go index 10c819c..304593b 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,6 @@ import ( "fmt" "log" "os" - "os/exec" "path/filepath" "runtime" // for detecting system architecture and platform "spitfire-builder/spitfire" @@ -25,7 +24,6 @@ var ( skipPatchUpdate bool run bool compress bool - buildPath string target string version string component string @@ -46,7 +44,6 @@ var ( ) func init() { - flag.StringVar(&buildPath, "p", "", "Path to the build directory") flag.StringVar(&target, "t", "", "Target location format: component-arch-release-platform") flag.BoolVar(&compress, "c", false, "Compress the build directory into a tar.gz file before uploading") flag.StringVar(&version, "v", "", "Specify version for the package. For nightly, use current date if not specified.") @@ -76,37 +73,6 @@ func printHelp() { os.Exit(0) } -// checkDependencies verifies if required dependencies are installed -func checkDependencies() error { - dependencies := map[string]string{ - "mercurial": "hg", - "python3": "python3", - "rsync": "rsync", - "git": "git", - } - - // Log the PATH for debugging - fmt.Printf("Current PATH: %s\n", os.Getenv("PATH")) - - for dep, cmd := range dependencies { - // Use LookPath to check availability - if _, err := exec.LookPath(cmd); err != nil { - return fmt.Errorf("dependency %s is not installed or not found in PATH. Please run: sudo apt install %s", dep, dep) - } - - // For python3, run a specific version check to confirm - if dep == "python3" { - out, err := exec.Command(cmd, "--version").Output() - if err != nil { - return fmt.Errorf("dependency %s is installed but not functional. Ensure 'python3 --version' works correctly: %v", dep, err) - } - fmt.Printf("Detected %s: %s", dep, string(out)) - } - } - - return nil -} - func main() { flag.Parse() @@ -117,7 +83,7 @@ func main() { // Only check dependencies if NOT skipping them if !skipDeps { - if err := checkDependencies(); err != nil { + if err := spitfire.CheckDependencies(); err != nil { log.Fatalf("System check failed: %v", err) } } @@ -135,28 +101,6 @@ func main() { } fmt.Printf("Initial working directory: %s\n", initialDir) - // Determine buildPath dynamically if not provided - if buildPath == "" { - sourcePath, err := spitfire.ResolvePath("./mozilla-central") - if err != nil { - log.Fatalf("Error resolving source path: %v", err) - } - - resolvedBuildPath, err := spitfire.ResolveBuildDir(sourcePath) - if err != nil { - log.Fatalf("Failed to detect build directory dynamically: %v", err) - } - buildPath = resolvedBuildPath - fmt.Printf("Automatically detected buildPath: %s\n", buildPath) - } else { - // Convert buildPath to absolute path - buildPath, err2 = spitfire.ResolvePath(buildPath) - if err2 != nil { - log.Fatalf("Failed to convert buildPath to absolute path: %v", err2) - } - fmt.Printf("Resolved buildPath: %s\n", buildPath) - } - if uploadPath != "" { uploadPath, err2 = spitfire.ResolvePath(uploadPath) if err2 != nil { @@ -170,7 +114,31 @@ func main() { } if compress || upload { - PackageAndUploadProcess() + // Resolve the build directory dynamically + fmt.Println("Resolving build directory dynamically...") + sourcePath, err := spitfire.ResolvePath("./mozilla-central") + if err != nil { + log.Fatalf("Error resolving source path: %v", err) + } + + buildDir, err := spitfire.ResolveBuildDir(sourcePath) + if err != nil { + log.Fatalf("Error resolving build directory dynamically: %v", err) + } + fmt.Printf("Resolved build directory: %s\n", buildDir) + + // Validate that the build directory exists + stat, err := os.Stat(buildDir) + if os.IsNotExist(err) { + log.Fatalf("Error: build directory does not exist at %s\n", buildDir) + } else if err != nil { + log.Fatalf("Error accessing build directory: %v\n", err) + } else if !stat.IsDir() { + log.Fatalf("Error: path exists but is not a directory: %s\n", buildDir) + } + + // Proceed with packaging and uploading + PackageAndUploadProcess(buildDir) } spitfire.PrintErrors() @@ -260,12 +228,12 @@ func BuildProcess() { } // PackageAndUploadProcess handles compressing, packaging, and uploading the build to SourceForge. -func PackageAndUploadProcess() { +func PackageAndUploadProcess(buildDir string) { // Restore working directory before performing SourceForge operations restoreWorkingDirectory() - pathToUse := buildPath + pathToUse := buildDir if upload && uploadPath != "" { pathToUse = uploadPath } @@ -308,6 +276,9 @@ func PackageAndUploadProcess() { fmt.Printf("Compression ratio: %.2f:1\n", compressionRatio) fmt.Printf("Compression efficiency: %.2f%%\n", efficiency) + // Display compressed directory path + fmt.Printf("Compressed dir: %s", pathToUse) + // If not uploading, we're done if !upload { return diff --git a/spitfire/build.go b/spitfire/build.go index f2a0825..aef2512 100644 --- a/spitfire/build.go +++ b/spitfire/build.go @@ -82,33 +82,12 @@ func DiscardChanges(sourcePath string) { func CleanBuild(sourcePath string, fullClean bool) { fmt.Println("Cleaning build...") - // Resolve the build directory dynamically - buildDir, err := ResolveBuildDir(sourcePath) - if err != nil { - errors = append(errors, fmt.Sprintf("Failed to resolve build directory: %v", err)) - return - } - // Revert uncommitted changes if err := runCommand("hg", "revert", "--all", "--no-backup"); err != nil { errors = append(errors, "Failed to revert changes in Mozilla repository.") } - // Paths for CLOBBER file and last build timestamp - clobberFile := filepath.Join(buildDir, "CLOBBER") - buildTimestampFile := filepath.Join(buildDir, ".last_build_timestamp") - - // Check if a clobber is required - clobberRequired := false - if _, err := os.Stat(clobberFile); err == nil { - clobberFileInfo, _ := os.Stat(clobberFile) - if buildInfo, err := os.Stat(buildTimestampFile); err != nil || clobberFileInfo.ModTime().After(buildInfo.ModTime()) { - clobberRequired = true - fmt.Println("CLOBBER file has been updated. Clobber is required.") - } - } - - if fullClean || clobberRequired { + if fullClean { // Perform full clean or clobber if necessary var machCmd string if runtime.GOOS == "windows" { @@ -181,7 +160,7 @@ func UpdatePatches(patchesDir, patchesRepo, sourcePath string) { // Handle platform-specific rsync command fmt.Println("Copying files from patches directory to Firefox source directory...") - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" || !isMsys2() { // Use robocopy for Windows if err := runCommand("robocopy", patchesDir, sourcePath, "*", "/S", "/XF", ".git", "/XD", ".git"); err != nil { errors = append(errors, "Failed to copy files (Windows robocopy).") diff --git a/spitfire/checks.go b/spitfire/checks.go index c0bb232..8a78440 100644 --- a/spitfire/checks.go +++ b/spitfire/checks.go @@ -6,44 +6,47 @@ import ( "os/exec" "path/filepath" "runtime" + "strings" ) -// CheckSystemDependencies ensures that required tools for building are installed. -func CheckSystemDependencies() error { - requiredTools := map[string]string{ - "git": "https://git-scm.com/download/win", // Git - "python": "https://www.python.org/downloads/", // Python - "pip3": "https://pip.pypa.io/en/stable/installing/", // Pip3 +// CheckDependencies ensures that all required tools and dependencies for the build are installed. +func CheckDependencies() error { + // Common dependencies + dependencies := map[string]string{ + "git": "https://git-scm.com/download/win", // Git + "python": "https://www.python.org/downloads/", // Python + "pip3": "https://pip.pypa.io/en/stable/installing/", // Pip3 + "mercurial": "https://www.mercurial-scm.org/", // Mercurial (hg) } - if runtime.GOOS == "windows" { - // Check for MozillaBuild installation + // Add platform-specific dependencies + if runtime.GOOS == "windows" || !isMsys2() { mozBuildPath := os.Getenv("MOZILLABUILD") if mozBuildPath == "" { - mozBuildPath = "C:\\mozilla-build" // Default to standard MozillaBuild path + mozBuildPath = "C:\\mozilla-build" // Default path for MozillaBuild } - - // Check if MozillaBuild exists at the specified location if !dirExists(mozBuildPath) { - requiredTools["mozbuild"] = "https://ftp.mozilla.org/pub/mozilla/libraries/win32/MozillaBuildSetup-Latest.exe" + dependencies["mozbuild"] = "https://ftp.mozilla.org/pub/mozilla/libraries/win32/MozillaBuildSetup-Latest.exe" } + } else if runtime.GOOS != "windows" || !isMsys2() { + dependencies["rsync"] = "https://rsync.samba.org/download.html" // rsync for non-Windows platforms } + // Check for missing tools missingTools := []string{} - - // Check for each required tool - for tool, downloadLink := range requiredTools { + for tool, downloadLink := range dependencies { if !isCommandAvailable(tool) { missingTools = append(missingTools, fmt.Sprintf("%s (Download: %s)", tool, downloadLink)) } } - // Special check for mach in the local source directory (mozilla-central) + // Special check for `mach` in the local source directory machPath := filepath.Join("mozilla-central", "mach") if !fileExists(machPath) { - missingTools = append(missingTools, fmt.Sprintf("mach (run from mozilla-central directory)")) + missingTools = append(missingTools, "mach (ensure you are in the mozilla-central directory)") } + // Report missing tools if any if len(missingTools) > 0 { fmt.Println("The following tools are missing and are required for the build:") for _, tool := range missingTools { @@ -76,3 +79,8 @@ func dirExists(path string) bool { } return info.IsDir() } + +// isMsys2 detects if the environment is MSYS2 by checking the MSYSTEM environment variable. +func isMsys2() bool { + return strings.Contains(strings.ToLower(os.Getenv("MSYSTEM")), "msys") +} diff --git a/spitfire/patch.go b/spitfire/patch.go index c767e38..8996f81 100644 --- a/spitfire/patch.go +++ b/spitfire/patch.go @@ -57,7 +57,7 @@ func ApplyPatches(sourcePath string, patchesRepo string, patchesPath string, pat } // Apply the patches using `run.sh` - applyCmd := exec.Command("go", "run", ".", "--path", sourcePath, "--patches", fullPatchesPath) applyCmd.Dir = patchesCloneDir // Run from the patches directory + applyCmd := exec.Command("go", "run", ".", "--path", sourcePath, "--patches", fullPatchesPath) applyCmd.Dir = patchesCloneDir // Run from the patches directory applyCmd.Stdout = os.Stdout applyCmd.Stderr = os.Stderr diff --git a/spitfire/upload.go b/spitfire/upload.go index d3b0ec1..d16cc32 100644 --- a/spitfire/upload.go +++ b/spitfire/upload.go @@ -26,16 +26,24 @@ type Config struct { func LoadConfig() (*Config, error) { file, err := os.Open("sourceforge_config.json") // Assuming a JSON config file if err != nil { - return nil, err + return nil, fmt.Errorf("failed to open config file: %v", err) } defer file.Close() config := &Config{} if err := json.NewDecoder(file).Decode(config); err != nil { - return nil, err + return nil, fmt.Errorf("failed to decode config file: %v", err) } - config.SFKeyPath = filepath.FromSlash(config.SFKeyPath) + // Normalize the SSH key path for the current OS + config.SFKeyPath = filepath.Clean(config.SFKeyPath) + + // Validate that the key file exists + if _, err := os.Stat(config.SFKeyPath); os.IsNotExist(err) { + return nil, fmt.Errorf("SSH key file not found at path: %s", config.SFKeyPath) + } else if err != nil { + return nil, fmt.Errorf("error accessing SSH key file: %v", err) + } return config, nil } @@ -74,7 +82,9 @@ func CompressDirectory(srcDir, dstFile string) error { if err != nil { return err } - header.Name = relPath + + // Normalize paths to UNIX-style for tar compatibility + header.Name = filepath.ToSlash(relPath) // Explicitly set the type flag for directories if fi.IsDir() {