diff --git a/main.go b/main.go index f2601cc..b780f39 100644 --- a/main.go +++ b/main.go @@ -46,33 +46,75 @@ var ( var errors []error // This is also in build.go. Great! This code is a mess. 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.BoolVar(&ignoreErrors, "ignore-errors", false, "Processes all steps even if errors occur") - flag.Bool("h", false, "Display help message") + 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.BoolVar(&ignoreErrors, "ignore-errors", false, "โš ๏ธ Processes all steps even if errors occur") + 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 -a") + fmt.Println("๐Ÿ“– Spitfire Build System Help") + fmt.Println("Usage: go run . [options]") + fmt.Println("") + fmt.Println("๐Ÿš€ Main Operations:") + fmt.Println("") + fmt.Println("๐Ÿ”ง Build Options:") + fmt.Println(" -a, -all Perform all steps (build, clean, update)") + fmt.Println(" -b, -build Build Spitfire") + fmt.Println(" -r, -run Run the project after build") + fmt.Println("") + fmt.Println("๐Ÿงน Clean Options:") + fmt.Println(" -clean Revert uncommitted changes") + fmt.Println(" -full-clean Remove all build artifacts (clobber)") + fmt.Println("") + fmt.Println("๐Ÿ”„ Update Options:") + fmt.Println(" -u, -update Update Mozilla repository") + fmt.Println(" -pre-patch Apply pre-build patches") + fmt.Println(" -post-patch Apply post-build patches") + fmt.Println(" -skip-patch-update Skip updating patches repository") + fmt.Println("") + fmt.Println("๐Ÿ“ฆ Package Options:") + fmt.Println(" -c, -compress Compress build directory to tar.gz") + fmt.Println(" -upload Upload compressed file to SourceForge") + fmt.Println(" -upload-path Path to file for upload") + fmt.Println("") + fmt.Println("โš™๏ธ Configuration Options:") + fmt.Println(" -t, -target Target format: component-arch-release-platform") + fmt.Println(" -v, -version Package version (default: date for nightly)") + fmt.Println(" -component Component name (default: browser)") + fmt.Println(" -arch Architecture (default: system arch)") + fmt.Println(" -release Release type (default: nightly)") + fmt.Println(" -platform Platform (default: system OS)") + fmt.Println("") + fmt.Println("๐Ÿ” Other Options:") + fmt.Println(" -skip-deps Skip system dependency checks") + fmt.Println(" -ignore-errors Continue despite errors") + fmt.Println(" -h, -help Show this help message") + fmt.Println("") + fmt.Println("๐Ÿ’ก Examples:") + fmt.Println(" Full build and upload: go run . -a -c -upload") + fmt.Println(" Just build: go run . -b") + fmt.Println(" Clean build: go run . -clean") + fmt.Println(" Upload existing: go run . -upload -upload-path=./build.tar.gz") + fmt.Println("") + fmt.Println("๐ŸŒ Project: https://weforge.xyz/Spitfire") os.Exit(0) } @@ -92,162 +134,178 @@ func main() { printHelp() } + fmt.Println("๐Ÿš€ Starting Spitfire build process...") + if !skipDeps { + fmt.Println("๐Ÿ” Checking system dependencies...") if err := spitfire.CheckDependencies(); err != nil { - handleError(fmt.Errorf("system check failed: %w", err)) + handleError(fmt.Errorf("โŒ system check failed: %w", err)) } + fmt.Println("โœ… All dependencies verified") } if version == "" && release == "nightly" { version = time.Now().Format("2006.01.02") + fmt.Printf("๐Ÿ“… Using nightly version: %s\n", version) } // Save the initial directory var err error initialDir, err = os.Getwd() if err != nil { - handleError(fmt.Errorf("failed to get current directory: %w", err)) + handleError(fmt.Errorf("โŒ failed to get current directory: %w", err)) } var buildDir string if all || buildFlag || prePatch || postPatch || clean || update || run { + fmt.Println("๐Ÿ—๏ธ Starting build process...") buildDir, err = BuildProcess() if err != nil { - handleError(fmt.Errorf("build process failed: %w", err)) + handleError(fmt.Errorf("โŒ build process failed: %w", err)) } else if buildDir == "" && (buildFlag || all || run) { - handleError(fmt.Errorf("build directory not found after build process")) + handleError(fmt.Errorf("โŒ build directory not found after build process")) } } if compress || upload { + fmt.Println("๐Ÿ“ฆ Packaging and uploading build...") if err := PackageAndUploadProcess(buildDir); err != nil { - handleError(fmt.Errorf("package/upload failed: %w", err)) + handleError(fmt.Errorf("โŒ package/upload failed: %w", err)) } } spitfire.PrintErrors() if len(errors) > 0 { - fmt.Fprintf(os.Stderr, "Exiting with %d error(s):\n", len(errors)) + fmt.Fprintf(os.Stderr, "โŒ Exiting with %d error(s):\n", len(errors)) for _, e := range errors { - fmt.Fprintf(os.Stderr, "- %v\n", e) + fmt.Fprintf(os.Stderr, " - %v\n", e) } os.Exit(1) } else { + fmt.Println("๐ŸŽ‰ All operations completed successfully!") os.Exit(0) } } func BuildProcess() (string, error) { + fmt.Println("๐Ÿ“‚ Resolving source path...") sourcePath, err := spitfire.ResolvePath("./mozilla-release") if err != nil { - return "", fmt.Errorf("resolve source path: %w", err) + return "", fmt.Errorf("โŒ resolve source path: %w", err) } var buildDir string if all { + fmt.Println("๐Ÿ”„ Running full build process...") if err := spitfire.DownloadSource(sourcePath, sourceRepo); err != nil { - return "", fmt.Errorf("download source: %w", err) + return "", fmt.Errorf("โŒ download source: %w", err) } if err := spitfire.DiscardChanges(sourcePath); err != nil { - return "", fmt.Errorf("discard changes: %w", err) + return "", fmt.Errorf("โŒ discard changes: %w", err) } if err := spitfire.CleanBuild(sourcePath, fullClean); err != nil { - return "", fmt.Errorf("clean build: %w", err) + return "", fmt.Errorf("โŒ clean build: %w", err) } if err := spitfire.UpdateRepo(sourcePath); err != nil { - return "", fmt.Errorf("update repo: %w", err) + return "", fmt.Errorf("โŒ update repo: %w", err) } if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { - return "", fmt.Errorf("pre-patch: %w", err) + return "", fmt.Errorf("โŒ pre-patch: %w", err) } if err := spitfire.Configure(sourcePath); err != nil { - return "", fmt.Errorf("configure: %w", err) + return "", fmt.Errorf("โŒ configure: %w", err) } if err := spitfire.Build(sourcePath); err != nil { - return "", fmt.Errorf("build: %w", err) + return "", fmt.Errorf("โŒ build: %w", err) } buildDir, err = spitfire.ResolveBuildDir(sourcePath) if err != nil { - return "", fmt.Errorf("resolve build dir: %w", err) + return "", fmt.Errorf("โŒ resolve build dir: %w", err) } if err := spitfire.ApplyPatches(buildDir, patchesRepo, "post-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { - return "", fmt.Errorf("post-patch: %w", err) + return "", fmt.Errorf("โŒ post-patch: %w", err) } if run { if err := spitfire.RunProject(sourcePath); err != nil { - return "", fmt.Errorf("run project: %w", err) + return "", fmt.Errorf("โŒ run project: %w", err) } } - fmt.Println("Spitfire build completed successfully.") + fmt.Println("โœ… Spitfire build completed successfully") } else if buildFlag { + fmt.Println("๐Ÿ”จ Running build process...") if prePatch { if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { - return "", fmt.Errorf("pre-patch: %w", err) + return "", fmt.Errorf("โŒ pre-patch: %w", err) } } if err := spitfire.Configure(sourcePath); err != nil { - return "", fmt.Errorf("configure: %w", err) + return "", fmt.Errorf("โŒ configure: %w", err) } if err := spitfire.Build(sourcePath); err != nil { - return "", fmt.Errorf("build: %w", err) + return "", fmt.Errorf("โŒ build: %w", err) } buildDir, err = spitfire.ResolveBuildDir(sourcePath) if err != nil { - return "", fmt.Errorf("resolve build dir: %w", err) + return "", fmt.Errorf("โŒ resolve build dir: %w", err) } if postPatch { if err := spitfire.ApplyPatches(buildDir, patchesRepo, "post-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { - return "", fmt.Errorf("post-patch: %w", err) + return "", fmt.Errorf("โŒ post-patch: %w", err) } } if run { if err := spitfire.RunProject(sourcePath); err != nil { - return "", fmt.Errorf("run project: %w", err) + return "", fmt.Errorf("โŒ run project: %w", err) } } - fmt.Println("Spitfire build completed successfully.") + fmt.Println("โœ… Spitfire build completed successfully") } else if clean { + fmt.Println("๐Ÿงน Cleaning build...") if err := spitfire.CleanBuild(sourcePath, fullClean); err != nil { - return "", fmt.Errorf("clean build: %w", err) + return "", fmt.Errorf("โŒ clean build: %w", err) } - fmt.Println("Cleaned Firefox build.") + fmt.Println("โœ… Firefox build cleaned") } else if update { + fmt.Println("๐Ÿ”„ Updating repository...") if err := spitfire.DownloadSource(sourcePath, sourceRepo); err != nil { - return "", fmt.Errorf("download source: %w", err) + return "", fmt.Errorf("โŒ download source: %w", err) } if err := spitfire.UpdateRepo(sourcePath); err != nil { - return "", fmt.Errorf("update repo: %w", err) + return "", fmt.Errorf("โŒ update repo: %w", err) } - fmt.Println("Mozilla repository updated.") + fmt.Println("โœ… Mozilla repository updated") } else if prePatch { + fmt.Println("๐Ÿ”ง Applying pre-compile patches...") if err := spitfire.DownloadSource(sourcePath, sourceRepo); err != nil { - return "", fmt.Errorf("download source: %w", err) + return "", fmt.Errorf("โŒ download source: %w", err) } if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { - return "", fmt.Errorf("pre-patch: %w", err) + return "", fmt.Errorf("โŒ pre-patch: %w", err) } - fmt.Println("Patches updated.") + fmt.Println("โœ… Pre-compile patches applied") } else if postPatch { + fmt.Println("๐Ÿ”ง Applying post-compile patches...") buildDir, err = spitfire.ResolveBuildDir(sourcePath) if err != nil { - return "", fmt.Errorf("resolve build dir: %w", err) + return "", fmt.Errorf("โŒ resolve build dir: %w", err) } if _, err := os.Stat(buildDir); err == nil { if err := spitfire.ApplyPatches(buildDir, patchesRepo, "post-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { - return "", fmt.Errorf("post-patch: %w", err) + return "", fmt.Errorf("โŒ post-patch: %w", err) } } if run { if err := spitfire.RunProject(sourcePath); err != nil { - return "", fmt.Errorf("run project: %w", err) + return "", fmt.Errorf("โŒ run project: %w", err) } } } else if run { + fmt.Println("๐Ÿš€ Running project...") if err := spitfire.RunProject(sourcePath); err != nil { - return "", fmt.Errorf("run project: %w", err) + return "", fmt.Errorf("โŒ run project: %w", err) } } @@ -255,69 +313,79 @@ func BuildProcess() (string, error) { } func PackageAndUploadProcess(buildDir string) error { + fmt.Println("๐Ÿ“ฆ Starting package and upload process...") restoreWorkingDirectory() pathToUse := buildDir if upload && uploadPath != "" { pathToUse = uploadPath + fmt.Printf(" โ”œโ”€ Using custom upload path: %s\n", pathToUse) } if pathToUse == "" { - return fmt.Errorf("no valid build or upload path provided") + return fmt.Errorf("โŒ no valid build or upload path provided") } + fmt.Println("๐Ÿ“ Calculating directory size...") uncompressedSize, err := spitfire.GetDirectorySize(pathToUse) if err != nil { - return fmt.Errorf("get directory size: %w", err) + return fmt.Errorf("โŒ get directory size: %w", err) } - fmt.Printf("Uncompressed directory size: %s\n", spitfire.BytesToHumanReadable(uncompressedSize)) + fmt.Printf(" โ”œโ”€ Uncompressed size: %s\n", spitfire.BytesToHumanReadable(uncompressedSize)) outputCompressedFile := filepath.Join(".", fmt.Sprintf( "%s-%s-%s-%s.tar.gz", component, arch, release, platform, )) + fmt.Printf(" โ”œโ”€ Output filename: %s\n", outputCompressedFile) if compress { + fmt.Println("๐Ÿ—œ๏ธ Compressing directory...") if err := spitfire.CompressDirectory(pathToUse, outputCompressedFile); err != nil { - return fmt.Errorf("compress directory: %w", err) + return fmt.Errorf("โŒ compress directory: %w", err) } - fmt.Printf("Build directory compressed to: %s\n", outputCompressedFile) + fmt.Printf("โœ… Build directory compressed to: %s\n", outputCompressedFile) } compressedSize, err := spitfire.GetFileSize(outputCompressedFile) if err != nil { - return fmt.Errorf("get compressed file size: %w", err) + return fmt.Errorf("โŒ get compressed file size: %w", err) } - fmt.Printf("Compressed file size: %s\n", spitfire.BytesToHumanReadable(compressedSize)) + fmt.Printf(" โ”œโ”€ Compressed size: %s\n", spitfire.BytesToHumanReadable(compressedSize)) compressionRatio, efficiency := spitfire.CalculateCompressionEfficiency(uncompressedSize, compressedSize) - fmt.Printf("Compression ratio: %.2f:1\n", compressionRatio) - fmt.Printf("Compression efficiency: %.2f%%\n", efficiency) - fmt.Printf("Compressed dir: %s\n", pathToUse) + fmt.Printf(" โ”œโ”€ Compression ratio: %.2f:1\n", compressionRatio) + fmt.Printf(" โ”œโ”€ Compression efficiency: %.2f%%\n", efficiency) + fmt.Printf(" โ””โ”€ Source directory: %s\n", pathToUse) if !upload { + fmt.Println("โฉ Upload skipped (not requested)") return nil } + fmt.Println("๐Ÿ”‘ Loading configuration...") config, err := spitfire.LoadConfig() if err != nil { - return fmt.Errorf("load config: %w", err) + return fmt.Errorf("โŒ load config: %w", err) } if _, err := os.Stat(outputCompressedFile); err != nil { - return fmt.Errorf("compressed file not found: %w", err) + return fmt.Errorf("โŒ compressed file not found: %w", err) } uploadDir := fmt.Sprintf("%s/%s/%s/%s", component, arch, release, version) + fmt.Printf("๐Ÿ“ค Uploading to: %s\n", uploadDir) if err := spitfire.Upload(config, outputCompressedFile, "/home/frs/project/spitfire-browser/"+uploadDir+"/"); err != nil { - return fmt.Errorf("upload compressed file: %w", err) + return fmt.Errorf("โŒ upload compressed file: %w", err) } - fmt.Println("Compressed file uploaded successfully.") + fmt.Println("โœ… Compressed file uploaded successfully") + fmt.Println("๐Ÿ“ฅ Downloading APPINDEX...") if err := spitfire.DownloadAPPINDEX(config, "/home/frs/project/spitfire-browser/"); err != nil { - fmt.Println("Failed to download APPINDEX. A new one will be created and uploaded.") + fmt.Println("โš ๏ธ Failed to download APPINDEX - creating new one") } + fmt.Println("๐Ÿ“ Updating APPINDEX...") if err := spitfire.PackageAPPINDEX( packageName, release, @@ -334,24 +402,27 @@ func PackageAndUploadProcess(buildDir string) error { platform, uploadDir, ); err != nil { - return fmt.Errorf("package APPINDEX: %w", err) + return fmt.Errorf("โŒ package APPINDEX: %w", err) } - fmt.Println("APPINDEX updated successfully.") + fmt.Println("โœ… APPINDEX updated successfully") + fmt.Println("๐Ÿงน Cleaning APPINDEX...") if err := spitfire.CleanAppIndex(); err != nil { - return fmt.Errorf("clean APPINDEX: %w", err) + return fmt.Errorf("โŒ clean APPINDEX: %w", err) } + fmt.Println("๐Ÿ“ค Uploading APPINDEX...") if err := spitfire.UploadAPPINDEX(config, "/home/frs/project/spitfire-browser/"); err != nil { - return fmt.Errorf("upload APPINDEX: %w", err) + return fmt.Errorf("โŒ upload APPINDEX: %w", err) } - fmt.Println("APPINDEX uploaded successfully.") + fmt.Println("โœ… APPINDEX uploaded successfully") return nil } func restoreWorkingDirectory() { + fmt.Println("โ†ฉ๏ธ Restoring working directory...") if err := os.Chdir(initialDir); err != nil { - log.Printf("Failed to restore working directory: %v", err) + log.Printf("โš ๏ธ Failed to restore working directory: %v", err) } } diff --git a/spitfire/appindex.go b/spitfire/appindex.go index a5038bd..6dfb0e3 100644 --- a/spitfire/appindex.go +++ b/spitfire/appindex.go @@ -27,32 +27,36 @@ func PackageAPPINDEX( platform, // e.g. "linux" remoteDir string, // e.g. "nightly/linux/amd64" ) error { + fmt.Println("๐Ÿ“ฆ Starting APPINDEX packaging...") // Construct a filename that matches what you compress & upload // Adjust this naming convention to match your compress step exactly! // Example: "spitfire-browser-nightly-amd64-linux.tar.gz" fileName := fmt.Sprintf("%s-%s-%s-%s.tar.gz", origin, arch, release, platform) + fmt.Printf(" โ”œโ”€ Generated filename: %s\n", fileName) - // If you have a real file on disk, you might call `calcChecksum(fileName)`. - // For demo, we call `calcChecksum` with a mock package name. + // Calculate checksum checksum := calcChecksum(fileName) + fmt.Printf(" โ”œโ”€ Calculated checksum: %s\n", checksum[:8]+"...") // Show partial checksum // Remove existing entry based on P, R, A, o, and p: removeExistingEntry(name, release, arch, origin, platform) // Use current Unix timestamp timestamp := time.Now().Unix() + fmt.Printf(" โ”œโ”€ Current timestamp: %d\n", timestamp) // 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) + log.Fatalf("โŒ Failed to open APPINDEX file: %v", err) } defer file.Close() // Build the SourceForge-based download URL: // https://downloads.sourceforge.net/project/spitfire-browser// downloadURL := fmt.Sprintf("https://downloads.sourceforge.net/project/spitfire-browser/%s/%s", remoteDir, fileName) + fmt.Printf(" โ”œโ”€ Download URL: %s\n", downloadURL) // Format the entry. entry := fmt.Sprintf(` @@ -85,11 +89,13 @@ func PackageAPPINDEX( // Trim leading newline to keep it clean entry = strings.TrimPrefix(entry, "\n") + // Write entry + fmt.Println("โœ๏ธ Writing new entry...") if _, err := file.WriteString(entry + "\n"); err != nil { - log.Fatalf("Failed to write to APPINDEX file: %v", err) + log.Fatalf("โŒ Failed to write to APPINDEX file: %v", err) } - fmt.Println("APPINDEX has been updated successfully.") + fmt.Println("๐ŸŽ‰ APPINDEX updated successfully") return nil } @@ -192,10 +198,12 @@ func removeExistingEntry(name, release, arch, origin, platform string) { // CleanAppIndex cleans up any orphaned "C:" entries and collapses excessive newlines func CleanAppIndex() error { + fmt.Println("๐Ÿงผ Cleaning APPINDEX file...") + // Read file contents content, err := os.ReadFile("./APPINDEX") if err != nil { - return fmt.Errorf("failed to read APPINDEX: %v", err) + return fmt.Errorf("โŒ Failed to read APPINDEX: %v", err) } // Split the file content into lines @@ -242,11 +250,12 @@ func CleanAppIndex() error { cleanedContent = strings.ReplaceAll(cleanedContent, "\n\n\n", "\n\n") // Write the cleaned content back to the file + fmt.Println("โœ๏ธ Writing cleaned content...") err = os.WriteFile("./APPINDEX", []byte(cleanedContent), 0644) if err != nil { - return fmt.Errorf("failed to write cleaned APPINDEX: %v", err) + return fmt.Errorf("โŒ Failed to write cleaned APPINDEX: %v", err) } - fmt.Println("APPINDEX cleaned successfully.") + fmt.Println("โœ… APPINDEX cleaned successfully") return nil } diff --git a/spitfire/build.go b/spitfire/build.go index 069dfa4..2fb9982 100644 --- a/spitfire/build.go +++ b/spitfire/build.go @@ -6,14 +6,21 @@ import ( "os/exec" "path/filepath" "runtime" + "sync" "time" ) // Array to store errors var errors []string +var ( + repoOperationsDone bool + repoMutex sync.Mutex +) + // SetGlobalEnv sets the MOZILLABUILD environment variable globally for the user (or system) func SetGlobalEnv(variable, value string, scope string) error { + fmt.Printf("โš™๏ธ Setting environment variable: %s=%s (scope: %s)\n", variable, value, scope) if runtime.GOOS == "windows" { var cmd *exec.Cmd if scope == "user" { @@ -21,18 +28,18 @@ func SetGlobalEnv(variable, value string, scope string) error { } else if scope == "system" { cmd = exec.Command("setx", variable, value, "/M") // Set for system (requires admin privileges) } else { - return fmt.Errorf("unknown scope: %s", scope) + return fmt.Errorf("โŒ unknown scope: %s", scope) } // Run the command out, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("failed to set environment variable %s=%s: %v\nOutput: %s", variable, value, err, string(out)) + return fmt.Errorf("โŒ failed to set environment variable %s=%s: %v\nOutput: %s", variable, value, err, string(out)) } - fmt.Printf("Successfully set %s=%s\n", variable, value) + fmt.Printf("โœ… Successfully set %s=%s\n", variable, value) return nil } else { - return fmt.Errorf("global environment variable setting is not supported on non-Windows systems") + return fmt.Errorf("โŒ global environment variable setting is not supported on non-Windows systems") } } @@ -45,49 +52,63 @@ func runCommand(command string, args ...string) error { return cmd.Run() } +func runCommandInDir(dir string, name string, arg ...string) error { + cmd := exec.Command(name, arg...) + cmd.Dir = dir + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + // Function to resolve paths using absolute path func ResolvePath(path string) (string, error) { + fmt.Printf("๐Ÿ“ Resolving path: %s\n", path) // Convert Unix-style slashes to the platform's native slashes path = filepath.FromSlash(path) // Get the absolute path absPath, err := filepath.Abs(path) if err != nil { - return "", fmt.Errorf("failed to resolve path: %s, error: %v", path, err) + return "", fmt.Errorf("โŒ failed to resolve path: %s, error: %v", path, err) } + fmt.Printf(" โ””โ”€ Resolved to: %s\n", absPath) return absPath, nil } // Function to download Mozilla source if not present func DownloadSource(sourcePath, sourceRepo string) error { + fmt.Println("๐Ÿ” Checking Mozilla source repository...") if _, err := os.Stat(sourcePath); os.IsNotExist(err) { - fmt.Println("Mozilla source not found. Cloning from repository...") + fmt.Println("๐Ÿ“ฅ Mozilla source not found - cloning repository...") if err := runCommand("hg", "clone", sourceRepo, sourcePath); err != nil { - return fmt.Errorf("failed to clone Mozilla repository: %w", err) + return fmt.Errorf("โŒ failed to clone Mozilla repository: %w", err) } + fmt.Println("โœ… Repository cloned successfully") } else { - fmt.Println("Mozilla source already exists.") + fmt.Println("โ„น๏ธ Mozilla source already exists") } return nil } // Function to discard uncommitted changes func DiscardChanges(sourcePath string) error { - fmt.Println("Discarding uncommitted changes...") + fmt.Println("๐Ÿงน Discarding uncommitted changes...") if err := runCommand("hg", "revert", "--all", "--no-backup", "-R", sourcePath); err != nil { - return fmt.Errorf("failed to revert changes: %w", err) + return fmt.Errorf("โŒ failed to revert changes: %w", err) } + fmt.Println("โœ… Changes discarded successfully") return nil } // Function to clean build func CleanBuild(sourcePath string, fullClean bool) error { - fmt.Println("Cleaning build...") + fmt.Println("๐Ÿงผ Cleaning build...") if err := runCommand("hg", "revert", "--all", "--no-backup", "-R", sourcePath); err != nil { - return fmt.Errorf("failed to revert changes: %w", err) + return fmt.Errorf("โŒ failed to revert changes: %w", err) } if fullClean { + fmt.Println("๐Ÿงน Performing full clean (clobber)...") machCmd := filepath.Join(sourcePath, "mach") if runtime.GOOS == "windows" { machCmd = filepath.Join(sourcePath, "mach.bat") @@ -95,117 +116,195 @@ func CleanBuild(sourcePath string, fullClean bool) error { cmd := exec.Command(machCmd, "clobber") cmd.Dir = sourcePath // Run in the source directory if err := cmd.Run(); err != nil { - return fmt.Errorf("failed to clean build: %w", err) + return fmt.Errorf("โŒ failed to clean build: %w", err) } - fmt.Println("Build artifacts cleaned successfully.") + fmt.Println("โœ… Build artifacts cleaned successfully") } return nil } -// Function to update Mozilla repository +// UpdateRepo updates an existing repository if not already done func UpdateRepo(sourcePath string) error { - fmt.Println("Updating Mozilla repository...") - if err := os.Chdir(sourcePath); err != nil { - return fmt.Errorf("failed to navigate to source directory: %w", err) + repoMutex.Lock() + defer repoMutex.Unlock() + + if repoOperationsDone { + fmt.Println("โ„น๏ธ Repository operations already completed - skipping update") + return nil } - if err := runCommand("hg", "pull", "-u"); err != nil { - return fmt.Errorf("failed to update repository: %w", err) + + const maxRetries = 3 + var success bool + + for attempt := 1; attempt <= maxRetries; attempt++ { + fmt.Printf("๐Ÿ”„ Update attempt %d/%d\n", attempt, maxRetries) + + if err := runCommandInDir(sourcePath, "hg", "pull", "-u"); err != nil { + fmt.Println("โŒ Update failed - removing corrupted repository") + os.RemoveAll(sourcePath) + if attempt < maxRetries { + time.Sleep(10 * time.Second) + } + continue + } + + success = true + break } - return nil + + if success { + repoOperationsDone = true + return nil + } + return fmt.Errorf("failed to update repository after %d attempts", maxRetries) +} + +// CloneRepo clones a fresh repository if not already done +func CloneRepo(sourcePath, url string) error { + repoMutex.Lock() + defer repoMutex.Unlock() + + if repoOperationsDone { + fmt.Println("โ„น๏ธ Repository operations already completed - skipping clone") + return nil + } + + const maxRetries = 3 + var success bool + + parentDir := filepath.Dir(sourcePath) + if err := os.MkdirAll(parentDir, 0755); err != nil { + return fmt.Errorf("failed to create parent directory: %w", err) + } + + for attempt := 1; attempt <= maxRetries; attempt++ { + fmt.Printf("๐Ÿ“ฅ Clone attempt %d/%d\n", attempt, maxRetries) + + if err := runCommand("hg", "clone", "--stream", url, sourcePath); err != nil { + fmt.Println("โŒ Clone failed - cleaning up") + os.RemoveAll(sourcePath) + if attempt < maxRetries { + time.Sleep(10 * time.Second) + } + continue + } + + success = true + break + } + + if success { + repoOperationsDone = true + return nil + } + return fmt.Errorf("failed to clone repository after %d attempts", maxRetries) } // Function to update patches func UpdatePatches(patchesDir, patchesRepo, sourcePath string) { - fmt.Println("Updating patches...") + fmt.Println("๐Ÿ”„ Updating patches...") if _, err := os.Stat(patchesDir); err == nil { - fmt.Println("Patches directory already exists. Cleaning and pulling updates...") + fmt.Println("โ„น๏ธ Patches directory exists - updating...") if err := os.Chdir(patchesDir); err != nil { - errors = append(errors, "Failed to navigate to patches directory.") + errors = append(errors, "โŒ Failed to navigate to patches directory") return } + + fmt.Println("๐Ÿงน Cleaning patches directory...") if err := runCommand("git", "clean", "-xdf"); err != nil { - errors = append(errors, "Failed to clean patches directory.") + errors = append(errors, "โŒ Failed to clean patches directory") } + + fmt.Println("๐Ÿ’พ Stashing local changes...") _ = runCommand("git", "stash", "push", "--include-untracked") + + fmt.Println("๐Ÿ“ฅ Fetching updates...") if err := runCommand("git", "fetch"); err != nil { - errors = append(errors, "Failed to fetch updates from patches repository.") + errors = append(errors, "โŒ Failed to fetch updates from patches repository") } + + fmt.Println("๐Ÿ”€ Rebasing changes...") 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.") + 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.") + errors = append(errors, "โŒ Failed to rebase updates from master branch") } } else { - errors = append(errors, "No valid branch (main or master) found in patches repository.") + errors = append(errors, "โŒ No valid branch (main or master) found in patches repository") return } + if runCommand("git", "stash", "list") == nil { + fmt.Println("โ†ฉ๏ธ Restoring stashed changes...") _ = runCommand("git", "stash", "pop") } else { - fmt.Println("No stash entries found, skipping pop.") + fmt.Println("โ„น๏ธ No stash entries found - skipping pop") } } else { - fmt.Println("Patches directory does not exist. Cloning repository...") + fmt.Println("๐Ÿ“ฅ Cloning patches repository...") if err := runCommand("git", "clone", patchesRepo, patchesDir); err != nil { - errors = append(errors, "Failed to clone patches repository.") + errors = append(errors, "โŒ Failed to clone patches repository") } } - // Handle platform-specific rsync command - fmt.Println("Copying files from patches directory to Firefox source directory...") + fmt.Println("๐Ÿ“‚ Copying files to source directory...") if runtime.GOOS == "windows" || !isMsys2() { - // Use robocopy for Windows + fmt.Println("๐Ÿ–ฅ๏ธ Using 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).") + errors = append(errors, "โŒ Failed to copy files (Windows robocopy)") } } else { - // Use rsync for Unix-like systems + fmt.Println("๐Ÿง Using rsync for Unix...") if err := runCommand("rsync", "-av", "--exclude=.git", patchesDir+"/", sourcePath+"/"); err != nil { - errors = append(errors, "Failed to copy files (rsync).") + errors = append(errors, "โŒ Failed to copy files (rsync)") } } } // Function to configure Spitfire func Configure(sourcePath string) error { - fmt.Println("Configuring Spitfire...") + fmt.Println("โš™๏ธ Configuring Spitfire...") if err := os.Chdir(sourcePath); err != nil { - return fmt.Errorf("failed to navigate to source directory: %w", err) + return fmt.Errorf("โŒ failed to navigate to source directory: %w", err) } machCmd := "./mach" if runtime.GOOS == "windows" { machCmd = ".\\mach" } + fmt.Println("๐Ÿ”ง Running mach configure...") return runCommand(machCmd, "configure") } // Function to build Spitfire func Build(sourcePath string) error { - fmt.Println("Building Spitfire...") + fmt.Println("๐Ÿ”จ Building Spitfire...") if err := os.Chdir(sourcePath); err != nil { - return fmt.Errorf("failed to navigate to source directory: %w", err) + return fmt.Errorf("โŒ failed to navigate to source directory: %w", err) } machCmd := "./mach" if runtime.GOOS == "windows" { machCmd = ".\\mach" } + fmt.Println("๐Ÿ—๏ธ Running mach build...") return runCommand(machCmd, "build") } // Function to run the project after build func RunProject(sourcePath string) error { - fmt.Println("Running the project...") + fmt.Println("๐Ÿš€ Running the project...") // Resolve the build directory + fmt.Println("๐Ÿ” Resolving build directory...") buildDir, err := ResolveBuildDir(sourcePath) if err != nil { - return fmt.Errorf("error resolving build directory: %w", err) + return fmt.Errorf("โŒ error resolving build directory: %w", err) } + fmt.Printf(" โ”œโ”€ Build directory: %s\n", buildDir) // List of possible binaries binaries := []string{"firefox", "Firefox", "spitfire", "Spitfire"} @@ -213,62 +312,66 @@ func RunProject(sourcePath string) error { var binaryPath string // Check for the existence of binaries + fmt.Println("๐Ÿ”Ž Searching for executable binary...") for _, binary := range binaries { binaryPath = filepath.Join(buildDir, binary) if _, err := os.Stat(binaryPath); err == nil { - fmt.Printf("Found binary: %s\n", binaryPath) + fmt.Printf("โœ… Found binary: %s\n", binaryPath) break } binaryPath = "" } if binaryPath == "" { - return fmt.Errorf("no suitable binary found to run the project") + return fmt.Errorf("โŒ no suitable binary found to run the project") } // Create a unique profile directory for each run profilePath := filepath.Join(buildDir, fmt.Sprintf("profile_%d", time.Now().UnixNano())) + fmt.Printf("๐Ÿ“ Creating temporary profile: %s\n", profilePath) if err := os.Mkdir(profilePath, 0755); err != nil { - return fmt.Errorf("failed to create profile directory: %w", err) + return fmt.Errorf("โŒ failed to create profile directory: %w", err) } // Run the binary with the new profile + fmt.Printf("โšก Launching: %s -no-remote -profile %s\n", binaryPath, profilePath) cmd := exec.Command(binaryPath, "-no-remote", "-profile", profilePath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin - fmt.Printf("Running binary: %s with new profile at %s\n", binaryPath, profilePath) + fmt.Printf("โœ… Running binary: %s with new profile at %s\n", binaryPath, profilePath) runErr := cmd.Run() - // Delete the profile directory after execution - fmt.Printf("Deleting profile directory: %s\n", profilePath) + // Clean up profile directory + fmt.Println("๐Ÿงน Cleaning up temporary profile...") if removeErr := os.RemoveAll(profilePath); removeErr != nil { - fmt.Printf("Warning: failed to delete profile directory: %v\n", removeErr) + fmt.Printf("โš ๏ธ Warning: failed to delete profile directory: %v\n", removeErr) } else { - fmt.Println("Profile directory deleted successfully.") + fmt.Println("โœ… Profile directory cleaned successfully") } if runErr != nil { - return fmt.Errorf("failed to run the project: %w", runErr) + return fmt.Errorf("โŒ failed to run the project: %w", runErr) } - fmt.Println("Binary execution finished successfully.") + fmt.Println("๐ŸŽ‰ Binary execution completed successfully") return nil } // ResolveBuildDir detects the build directory dynamically func ResolveBuildDir(sourcePath string) (string, error) { + fmt.Println("๐Ÿ” Detecting build directory...") // The expected build directory pattern globPattern := filepath.Join(sourcePath, "obj-*") // Find matching directories matches, err := filepath.Glob(globPattern) if err != nil { - return "", fmt.Errorf("error resolving build directory: %v", err) + return "", fmt.Errorf("โŒ error resolving build directory: %v", err) } if len(matches) == 0 { - return "", fmt.Errorf("build directory not found under %s", sourcePath) + return "", fmt.Errorf("โŒ build directory not found under %s", sourcePath) } full := filepath.Join(matches[0], "dist", "bin") @@ -279,9 +382,9 @@ func ResolveBuildDir(sourcePath string) (string, error) { // Function to print collected errors func PrintErrors() { if len(errors) > 0 { - fmt.Println("The following errors occurred during execution:") + fmt.Println("โŒ The following errors occurred during execution:") for _, err := range errors { - fmt.Printf("- %s\n", err) + fmt.Printf(" - %s\n", err) } } } diff --git a/spitfire/checks.go b/spitfire/checks.go index 15d79b4..4966c94 100644 --- a/spitfire/checks.go +++ b/spitfire/checks.go @@ -7,16 +7,29 @@ import ( "path/filepath" "runtime" "strings" + "sync" +) + +var ( + reportedCommands = make(map[string]bool) + reportMutex sync.Mutex ) // CheckDependencies ensures that all required tools and dependencies for the build are installed. func CheckDependencies() error { + // Just to make sure, reset reported commands at start + reportMutex.Lock() + reportedCommands = make(map[string]bool) + reportMutex.Unlock() + // Common dependencies dependencies := map[string]string{ "git": "https://git-scm.com/downloads", // Git "python": "https://www.python.org/downloads/", // Python "pip3": "https://pip.pypa.io/en/stable/installing/", // Pip3 "hg": "https://www.mercurial-scm.org/", // Mercurial (hg) + "rustc": "https://www.rust-lang.org/tools/install", // Rust compiler + "cargo": "https://www.rust-lang.org/tools/install", // Cargo package manager } // Add platform-specific dependencies @@ -40,10 +53,44 @@ func CheckDependencies() error { } } + // Check Rust version if installed + if isCommandAvailable("rustc") { + _, err := getCommandOutput("rustc", "--version") + if err != nil { + missingTools = append(missingTools, "rustc (version check failed)") + } + } + // Check for `mach` script in the source directory machPath := filepath.Join("mozilla-release", "mach") + repoExists, err := CheckRepoExists("mozilla-release") + if err != nil { + // Repository exists but is corrupted + fmt.Println("โš ๏ธ Existing repository appears corrupted") + } + if !fileExists(machPath) { - missingTools = append(missingTools, "mach (ensure you are in the mozilla-release directory and it is built)") + if len(missingTools) == 0 { + // Only mach is missing and other dependencies are satisfied - try to clone + fmt.Println("โš ๏ธ mach script missing - attempting to clone repository...") + if err := CloneRepo("mozilla-release", "https://hg.mozilla.org/releases/mozilla-release/"); err != nil { + missingTools = append(missingTools, "mach (failed to automatically clone repository)") + } else { + // Verify mach exists after clone + if !fileExists(machPath) { + missingTools = append(missingTools, "mach (repository cloned but mach still missing)") + } else { + fmt.Println("โœ… Successfully cloned repository and found mach script") + } + } + } else { + // Other tools are also missing - just report mach as missing + if !repoExists { + missingTools = append(missingTools, "mach (repository not found)") + } else { + missingTools = append(missingTools, "mach (found repository but mach is missing)") + } + } } // Report missing tools if any @@ -59,16 +106,42 @@ func CheckDependencies() error { return nil } +// CheckRepoExists verifies if the Mozilla repository exists and is valid +func CheckRepoExists(sourcePath string) (bool, error) { + hgDir := filepath.Join(sourcePath, ".hg") + if _, err := os.Stat(hgDir); os.IsNotExist(err) { + return false, nil + } + return true, nil +} + // isCommandAvailable checks if a command/tool is available on the system. func isCommandAvailable(command string) bool { path, err := exec.LookPath(command) if err != nil { return false } - fmt.Printf("Command '%s' found at path '%s'\n", command, path) + + reportMutex.Lock() + defer reportMutex.Unlock() + + if !reportedCommands[command] { + fmt.Printf("Command '%s' found at path '%s'\n", command, path) + reportedCommands[command] = true + } return true } +// Helper function to get command output +func getCommandOutput(name string, arg ...string) (string, error) { + cmd := exec.Command(name, arg...) + output, err := cmd.CombinedOutput() + if err != nil { + return "", err + } + return string(output), nil +} + // fileExists checks if a file exists at the given path. func fileExists(path string) bool { _, err := os.Stat(path) diff --git a/spitfire/patch.go b/spitfire/patch.go index 8996f81..33e0eec 100644 --- a/spitfire/patch.go +++ b/spitfire/patch.go @@ -10,62 +10,68 @@ import ( // ApplyPatches handles cloning, updating, and applying patches func ApplyPatches(sourcePath string, patchesRepo string, patchesPath string, patchesCloneDir string, skipUpdateRepo bool) error { // Define the full patches path - fullPatchesPath := filepath.Join(patchesCloneDir, patchesPath) // Cleaned path without double slashes + fullPatchesPath := filepath.Join(patchesCloneDir, patchesPath) - fmt.Printf("Source Path: %s\n", sourcePath) - fmt.Printf("Patches Clone Directory: %s\n", patchesCloneDir) - fmt.Printf("Patches Repository URL: %s\n", patchesRepo) - fmt.Printf("Patches Path: %s\n", fullPatchesPath) + // Print configuration with consistent style + fmt.Printf("๐Ÿ”ง Config:\n") + fmt.Printf(" โ”œโ”€ Source Path: %s\n", sourcePath) + fmt.Printf(" โ”œโ”€ Patches Clone Dir: %s\n", patchesCloneDir) + fmt.Printf(" โ”œโ”€ Patches Repo: %s\n", patchesRepo) + fmt.Printf(" โ””โ”€ Patches Path: %s\n", fullPatchesPath) // Check if the patches directory already exists if _, err := os.Stat(patchesCloneDir); os.IsNotExist(err) { // Clone the patches repository - fmt.Println("Patches directory does not exist. Proceeding to clone.") + fmt.Println("๐Ÿ“ฅ Cloning patches repository...") cmdClone := exec.Command("git", "clone", patchesRepo, patchesCloneDir) cmdClone.Stdout = os.Stdout cmdClone.Stderr = os.Stderr if err := cmdClone.Run(); err != nil { - return fmt.Errorf("failed to clone patches repository: %v", err) + return fmt.Errorf("โŒ Failed to clone patches repository: %v", err) } - fmt.Println("Patches repository cloned successfully.") + fmt.Println("โœ… Patches repository cloned successfully") } else { if !skipUpdateRepo { - // If the directory exists, fetch and pull the latest changes - fmt.Println("Patches directory already exists. Fetching latest changes.") + fmt.Println("๐Ÿ”„ Updating patches repository...") + + // Fetch updates cmdFetch := exec.Command("git", "fetch", "--all") cmdFetch.Dir = patchesCloneDir cmdFetch.Stdout = os.Stdout cmdFetch.Stderr = os.Stderr if err := cmdFetch.Run(); err != nil { - return fmt.Errorf("failed to fetch updates for patches repository: %v", err) + return fmt.Errorf("โŒ Failed to fetch updates: %v", err) } + // Reset to latest cmdReset := exec.Command("git", "reset", "--hard", "origin/main") cmdReset.Dir = patchesCloneDir cmdReset.Stdout = os.Stdout cmdReset.Stderr = os.Stderr if err := cmdReset.Run(); err != nil { - return fmt.Errorf("failed to reset patches repository to latest state: %v", err) + return fmt.Errorf("โŒ Failed to reset repository: %v", err) } - fmt.Println("Patches repository updated successfully.") + fmt.Println("โœ… Patches repository updated") + } else { + fmt.Println("โฉ Skipping patches update (requested by user)") } } // Verify the patches directory exists if _, err := os.Stat(fullPatchesPath); os.IsNotExist(err) { - return fmt.Errorf("patches path not found: %s", fullPatchesPath) + return fmt.Errorf("โŒ Patches path not found: %s", fullPatchesPath) } // Apply the patches using `run.sh` applyCmd := exec.Command("go", "run", ".", "--path", sourcePath, "--patches", fullPatchesPath) - applyCmd.Dir = patchesCloneDir // Run from the patches directory + applyCmd.Dir = patchesCloneDir applyCmd.Stdout = os.Stdout applyCmd.Stderr = os.Stderr - fmt.Println("Applying patches...") + if err := applyCmd.Run(); err != nil { - return fmt.Errorf("failed to apply patches: %v", err) + return fmt.Errorf("โŒ Failed to apply patches: %v", err) } - fmt.Println("Patches applied successfully.") + fmt.Println("๐ŸŽ‰ Patches applied successfully") return nil } diff --git a/spitfire/upload.go b/spitfire/upload.go index a0783c5..abb8d89 100644 --- a/spitfire/upload.go +++ b/spitfire/upload.go @@ -26,52 +26,57 @@ type Config struct { // Load the SourceForge configuration from a file func LoadConfig() (*Config, error) { + fmt.Println("๐Ÿ”‘ Loading SourceForge configuration...") + file, err := os.Open("sourceforge_config.json") if err != nil { - return nil, fmt.Errorf("failed to open config file: %v", 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, fmt.Errorf("failed to decode config file: %v", err) + return nil, fmt.Errorf("โŒ failed to decode config file: %v", err) } - // Expand tilde manually + fmt.Println("๐Ÿ” Expanding SSH key path...") expandedPath, err := homedir.Expand(config.SFKeyPath) if err != nil { - return nil, fmt.Errorf("failed expanding key path: %v", err) + return nil, fmt.Errorf("โŒ failed expanding key path: %v", err) } config.SFKeyPath = filepath.Clean(expandedPath) - // Validate that the key file exists + fmt.Println("๐Ÿ” Validating SSH key...") if _, err := os.Stat(config.SFKeyPath); os.IsNotExist(err) { - return nil, fmt.Errorf("SSH key file not found at path: %s", config.SFKeyPath) + 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 nil, fmt.Errorf("โŒ error accessing SSH key file: %v", err) } + fmt.Println("โœ… Configuration loaded successfully") 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 + fmt.Printf("๐Ÿ—œ๏ธ Compressing directory: %s โ†’ %s\n", srcDir, dstFile) + + fmt.Println("๐Ÿ“„ Creating destination file...") f, err := os.Create(dstFile) if err != nil { - return fmt.Errorf("could not create file %s: %v", dstFile, err) + return fmt.Errorf("โŒ could not create file %s: %v", dstFile, err) } defer f.Close() - // Create a new gzip writer + fmt.Println("๐Ÿ“ฆ Initializing gzip writer...") gw := gzip.NewWriter(f) defer gw.Close() - // Create a new tar writer with PAX format for large file support + fmt.Println("๐Ÿ“ฆ Initializing tar writer (PAX format)...") tw := tar.NewWriter(gw) defer tw.Close() - // Walk through the source directory and add files to the tar archive + fmt.Println("๐Ÿ” Walking directory structure...") err = filepath.Walk(srcDir, func(file string, fi os.FileInfo, err error) error { if err != nil { return err @@ -130,46 +135,58 @@ func CompressDirectory(srcDir, dstFile string) error { }) if err != nil { - return fmt.Errorf("error walking the source directory %s: %v", srcDir, err) + return fmt.Errorf("โŒ error walking the source directory %s: %v", srcDir, err) } + fmt.Println("โœ… Compression completed successfully") return nil } // Upload the file to SourceForge, ensuring the local directory structure is created and uploaded func Upload(config *Config, buildPath, remoteDir string) error { + fmt.Println("๐Ÿ“ค Starting upload process...") + // Generate a random hash for the temp directory name + fmt.Println("๐Ÿ”’ Generating random hash for temp directory...") randomHash, err := generateRandomHash(8) // 8 bytes = 16 hex characters if err != nil { - return fmt.Errorf("failed to generate random hash: %v", err) + return fmt.Errorf("โŒ failed to generate random hash: %v", err) } // Create a temporary directory with the random hash appended + fmt.Printf("๐Ÿ“‚ Creating temporary directory with hash: %s...\n", randomHash) tmpDir, err := os.MkdirTemp("", "spitfire-upload-"+randomHash) if err != nil { - return fmt.Errorf("failed to create temporary directory: %v", err) + return fmt.Errorf("โŒ failed to create temporary directory: %v", err) } // Create the required local directory structure inside the temporary directory + fmt.Printf("๐Ÿ“ Creating local directory structure: %s...\n", remoteDir) 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) + return fmt.Errorf("โŒ failed to create local directory structure: %v", err) } // Move the build file to the local directory structure + fmt.Printf("๐Ÿ“ฆ Copying build file to temp location: %s...\n", filepath.Base(buildPath)) 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) + 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) + fmt.Printf("๐Ÿš€ Uploading %s to SourceForge (%s)...\n", filepath.Base(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() + if err := scpCmd.Run(); err != nil { + return fmt.Errorf("โŒ upload failed: %v", err) + } + + fmt.Println("โœ… Upload completed successfully") + return nil } // Helper function to generate a random hash @@ -206,7 +223,7 @@ func copyFile(src, dst string) error { // Download the APPINDEX file from SourceForge func DownloadAPPINDEX(config *Config, remoteDir string) error { - fmt.Println("Downloading APPINDEX from SourceForge...") + fmt.Println("๐Ÿ“ฅ Downloading APPINDEX from SourceForge...") // Construct the correct path without double slashes remoteAPPINDEXPath := filepath.Join(remoteDir, "APPINDEX") @@ -220,23 +237,27 @@ func DownloadAPPINDEX(config *Config, remoteDir string) error { 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.") + fmt.Println("โ„น๏ธ APPINDEX file not found - will create new one") 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 + return fmt.Errorf("โŒ failed to download APPINDEX: %v", err) // Fail for other types of errors } - fmt.Println("APPINDEX downloaded successfully.") + 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...") + 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() + if err := cmd.Run(); err != nil { + return fmt.Errorf("โŒ failed to upload APPINDEX: %v", err) + } + fmt.Println("โœ… APPINDEX uploaded successfully") + return nil } // GetDirectorySize calculates the total size of all files in a directory