package main import ( "flag" "fmt" "log" "os" "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 fullClean bool update bool prePatch bool postPatch bool skipPatchUpdate bool run bool compress bool target string version string component string arch string release string platform string upload bool uploadPath string sourceRepo = "https://hg.mozilla.org/releases/mozilla-release" patchesRepo = "https://weforge.xyz/Spitfire/Patcher.git" url = "https://spitfirebrowser.xyz/" licence = "AGPL-3.0" name = "Spitfire" packageName = "spitfire-browser" maintainer = "Internet Addict" initialDir string skipDeps bool ) func init() { 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, "Revert uncommitted changes without removing build artifacts") flag.BoolVar(&fullClean, "full-clean", false, "Perform a full clean by removing all build artifacts (clobber)") 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.BoolVar(&skipDeps, "skip-deps", false, "Skip checking for required system dependencies") 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-release/obj-x86_64-pc-linux-gnu/dist/bin -a") os.Exit(0) } func main() { flag.Parse() if flag.Lookup("h").Value.(flag.Getter).Get().(bool) { printHelp() } // Only check dependencies if NOT skipping them if !skipDeps { if err := spitfire.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) 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 { // Resolve the build directory dynamically fmt.Println("Resolving build directory dynamically...") sourcePath, err := spitfire.ResolvePath("./mozilla-release") 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() } // Update the BuildProcess function to pass patchesRepo func BuildProcess() { sourcePath, err := spitfire.ResolvePath("./mozilla-release") if err != nil { log.Fatalf("Error resolving source path: %v", err) } if all { spitfire.DownloadSource(sourcePath, sourceRepo) spitfire.DiscardChanges(sourcePath) spitfire.CleanBuild(sourcePath, fullClean) 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) // Detect the build directory dynamically buildDir, err := spitfire.ResolveBuildDir(sourcePath) if err != nil { log.Fatalf("Error resolving build directory: %v", err) } 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) // Detect the build directory dynamically buildDir, err := spitfire.ResolveBuildDir(sourcePath) if err != nil { log.Fatalf("Error resolving build directory: %v", err) } 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, fullClean) 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 { // Detect the build directory dynamically buildDir, err := spitfire.ResolveBuildDir(sourcePath) if err != nil { log.Fatalf("Error resolving build directory: %v", err) } 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(buildDir string) { // Restore working directory before performing SourceForge operations restoreWorkingDirectory() pathToUse := buildDir if upload && uploadPath != "" { pathToUse = uploadPath } if pathToUse == "" { log.Fatalf("Error: no valid build or upload path provided.") } // Calculate & display uncompressed size uncompressedSize, err := spitfire.GetDirectorySize(pathToUse) if err != nil { log.Fatalf("Failed to calculate uncompressed size: %v", err) } fmt.Printf("Uncompressed directory size: %s\n", spitfire.BytesToHumanReadable(uncompressedSize)) // Create the tar.gz name, e.g. "browser-amd64-nightly-linux.tar.gz" outputCompressedFile := filepath.Join(".", fmt.Sprintf( "%s-%s-%s-%s.tar.gz", component, arch, release, platform, )) // Compress if requested 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) } // Compressed size compressedSize, err := spitfire.GetFileSize(outputCompressedFile) if err != nil { log.Fatalf("Failed to get compressed file size: %v", err) } fmt.Printf("Compressed file size: %s\n", spitfire.BytesToHumanReadable(compressedSize)) // Show compression ratio & efficiency compressionRatio, efficiency := spitfire.CalculateCompressionEfficiency(uncompressedSize, compressedSize) 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 } // Load SourceForge config config, err := spitfire.LoadConfig() if err != nil { log.Fatalf("Failed to load SourceForge config: %v", err) } // Check tarball existence if _, err := os.Stat(outputCompressedFile); err != nil { log.Fatalf("No compressed file found to upload: %v", err) } // The subdirectory path in the SF project // e.g. "browser/amd64/nightly/2024.12.23" uploadDir := fmt.Sprintf("%s/%s/%s/%s", component, arch, release, version) // 1) Upload the file to SourceForge once err = spitfire.Upload(config, outputCompressedFile, "/home/frs/project/spitfire-browser/"+uploadDir+"/") if err != nil { log.Fatalf("Failed to upload compressed file: %v", err) } fmt.Println("Compressed file uploaded successfully.") // 2) Download existing APPINDEX or create new err = spitfire.DownloadAPPINDEX(config, "/home/frs/project/spitfire-browser/") if err != nil { fmt.Println("Failed to download APPINDEX. A new one will be created and uploaded.") } // 3) Update the APPINDEX err = spitfire.PackageAPPINDEX( packageName, // e.g. "spitfire-browser" release, // e.g. "nightly" version, // e.g. "2024.12.23" arch, fmt.Sprintf("%d", compressedSize), fmt.Sprintf("%d", uncompressedSize), name, // e.g. "Spitfire" url, licence, component, maintainer, "", // dependencies platform, uploadDir, ) if err != nil { log.Fatalf("Failed to update APPINDEX: %v", err) } fmt.Println("APPINDEX updated successfully.") // 4) Clean if err := spitfire.CleanAppIndex(); err != nil { log.Fatalf("Failed to clean APPINDEX: %v", err) } // 5) Upload the updated APPINDEX 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) }