package main import ( "flag" "fmt" "log" "os" "os/exec" "path/filepath" "runtime" // for detecting system architecture and platform "spitfire-builder/spitfire" "time" //"errors" ) var ( // Define all flags as package-level variables all bool buildFlag bool clean bool update bool prePatch bool postPatch bool skipPatchUpdate bool run bool compress bool buildPath string target string version string component string arch string release string platform string upload bool uploadPath string sourceRepo = "https://hg.mozilla.org/mozilla-central" patchesRepo = "https://weforge.xyz/Spitfire/Browser.git" url = "https://spitfirebrowser.xyz/" licence = "AGPL-3.0" name = "Spitfire" maintainer = "Internet Addict" initialDir string ) 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.") flag.StringVar(&component, "component", "browser", "Component name (default: browser)") flag.StringVar(&arch, "arch", runtime.GOARCH, "Architecture (default: system architecture)") flag.StringVar(&release, "release", "nightly", "Release type (default: nightly)") flag.StringVar(&platform, "platform", runtime.GOOS, "Platform (default: system platform)") flag.BoolVar(&all, "a", false, "Perform all steps (build, clean, update)") flag.BoolVar(&buildFlag, "b", false, "Build Spitfire") flag.BoolVar(&clean, "clean", false, "Clean build") flag.BoolVar(&update, "u", false, "Update Mozilla repository") flag.BoolVar(&prePatch, "pre-patch", false, "Apply pre-build patches") flag.BoolVar(&postPatch, "post-patch", false, "Apply post-build patches") flag.BoolVar(&skipPatchUpdate, "skip-patch-update", false, "Skip updating the patches repository") flag.BoolVar(&run, "r", false, "Run the project after build") flag.BoolVar(&upload, "upload", false, "Upload the compressed build file to SourceForge") flag.StringVar(&uploadPath, "upload-path", "", "Path to the file to upload if no build present") flag.Bool("h", false, "Display help message") } func printHelp() { fmt.Println("Usage: go run . -p= -t= [-c|--compress] [-v|--version=] [-component=] [-arch=] [-release=] [-platform=]") flag.PrintDefaults() fmt.Println("Example: go run . --upload -c --upload-path=./mozilla-central/obj-x86_64-pc-linux-gnu/dist/bin -a") 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() if flag.Lookup("h").Value.(flag.Getter).Get().(bool) { printHelp() } // Check system dependencies if err := checkDependencies(); err != nil { log.Fatalf("System check failed: %v", err) } // Set version to current date if it's empty and release is nightly if version == "" && release == "nightly" { version = time.Now().Format("2006.01.02") // Set version to current date if nightly } // Save the initial directory var err2 error initialDir, err2 = os.Getwd() if err2 != nil { log.Fatalf("Failed to get current working directory: %v", err2) } fmt.Printf("Initial working directory: %s\n", initialDir) // Convert buildPath and uploadPath to absolute paths if buildPath != "" { 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 { log.Fatalf("Failed to convert uploadPath to absolute path: %v", err2) } fmt.Printf("Resolved uploadPath: %s\n", uploadPath) } if all || buildFlag || prePatch || postPatch || clean || update || run { BuildProcess() } if compress || upload { PackageAndUploadProcess() } spitfire.PrintErrors() } // Update the BuildProcess function to pass patchesRepo func BuildProcess() { sourcePath, err := spitfire.ResolvePath("./mozilla-central") if err != nil { log.Fatalf("Error resolving source path: %v", err) } // Detect the build directory dynamically buildDir, err := spitfire.ResolveBuildDir(sourcePath) if err != nil { log.Fatalf("Error resolving build directory: %v", err) } if all { spitfire.DownloadSource(sourcePath, sourceRepo) spitfire.DiscardChanges(sourcePath) spitfire.CleanBuild(sourcePath) spitfire.UpdateRepo(sourcePath) if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { log.Fatalf("Error during patch application: %v", err) } spitfire.Configure(sourcePath) spitfire.Build(sourcePath) if err := spitfire.ApplyPatches(buildDir, patchesRepo, "post-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { log.Fatalf("Error during patch application: %v", err) } if run { spitfire.RunProject(sourcePath) } fmt.Println("Spitfire build completed successfully.") } else if buildFlag { if prePatch { if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { log.Fatalf("Error during patch application: %v", err) } } spitfire.Configure(sourcePath) spitfire.Build(sourcePath) if postPatch { if err := spitfire.ApplyPatches(buildDir, patchesRepo, "post-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { log.Fatalf("Error during patch application: %v", err) } } if run { spitfire.RunProject(sourcePath) } fmt.Println("Spitfire build completed successfully.") } else if clean { spitfire.CleanBuild(sourcePath) fmt.Println("Cleaned Firefox build.") } else if update { spitfire.DownloadSource(sourcePath, sourceRepo) spitfire.UpdateRepo(sourcePath) fmt.Println("Mozilla repository updated.") } else if prePatch { spitfire.DownloadSource(sourcePath, sourceRepo) if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { log.Fatalf("Error during patch application: %v", err) } fmt.Println("Patches updated.") } else if postPatch { if _, err := os.Stat(buildDir); err == nil { if err := spitfire.ApplyPatches(buildDir, patchesRepo, "post-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { log.Fatalf("Error during patch application: %v", err) } } if run { spitfire.RunProject(sourcePath) } } else if run { spitfire.RunProject(sourcePath) } } // PackageAndUploadProcess handles compressing, packaging, and uploading the build to SourceForge. func PackageAndUploadProcess() { // Restore working directory before performing SourceForge operations restoreWorkingDirectory() pathToUse := buildPath if upload && uploadPath != "" { pathToUse = uploadPath } if pathToUse == "" { log.Fatalf("Error: no valid build or upload path provided.") } // // This is stupid, it wait for the path to exist (up to a maximum wait time) // err := waitForPath(pathToUse, 60, 5) // Max 60 seconds, checking every 5 seconds // if err != nil { // log.Fatalf("Error: Build path or upload path not found: %v", err) // } uncompressedSize, err := spitfire.GetDirectorySize(pathToUse) if err != nil { log.Fatalf("Failed to calculate uncompressed size: %v", err) } fmt.Printf("Uncompressed directory size: %d bytes\n", uncompressedSize) outputCompressedFile := filepath.Join(".", fmt.Sprintf("%s-%s-%s-%s.tar.gz", component, arch, release, platform)) if compress { err := spitfire.CompressDirectory(pathToUse, outputCompressedFile) if err != nil { log.Fatalf("Failed to compress build directory: %v", err) } fmt.Printf("Build directory compressed to: %s\n", outputCompressedFile) } compressedSize, err := spitfire.GetFileSize(outputCompressedFile) if err != nil { log.Fatalf("Failed to get compressed file size: %v", err) } fmt.Printf("Compressed file size: %d bytes\n", compressedSize) if upload { config, err := spitfire.LoadConfig() if err != nil { log.Fatalf("Failed to load SourceForge config: %v", err) } if _, err := os.Stat(outputCompressedFile); err == nil { err = spitfire.Upload(config, outputCompressedFile, "/home/frs/project/spitfire-browser/"+component+"/"+arch+"/"+release+"/"+version+"/") if err != nil { log.Fatalf("Failed to upload compressed file: %v", err) } fmt.Println("Compressed file uploaded successfully.") } else { log.Fatalf("No compressed file found to upload.") } err = spitfire.DownloadAPPINDEX(config, "/home/frs/project/spitfire-browser/") if err != nil { fmt.Println("Failed to download APPINDEX. A new APPINDEX will be created and uploaded.") } err = spitfire.PackageAPPINDEX( name, release, version, arch, fmt.Sprintf("%d", compressedSize), fmt.Sprintf("%d", uncompressedSize), "Spitfire build", url, licence, component, maintainer, "", platform, ) if err != nil { log.Fatalf("Failed to update APPINDEX: %v", err) } fmt.Println("APPINDEX updated successfully.") if err := spitfire.CleanAppIndex(); err != nil { log.Fatalf("Failed to clean APPINDEX: %v", err) } err = spitfire.UploadAPPINDEX(config, "/home/frs/project/spitfire-browser/") if err != nil { log.Fatalf("Failed to upload updated APPINDEX: %v", err) } fmt.Println("APPINDEX uploaded successfully.") } } // restoreWorkingDirectory restores the initial working directory after any operation that might change it. func restoreWorkingDirectory() { err := os.Chdir(initialDir) if err != nil { log.Fatalf("Failed to restore the working directory: %v", err) } fmt.Printf("Restored working directory to: %s\n", initialDir) }