added automatic mozilla-release download
This commit is contained in:
parent
e5c95924d5
commit
a13f43086f
6 changed files with 477 additions and 194 deletions
237
main.go
237
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=<path-to-build> -t=<target> [-c|--compress] [-v|--version=<version>] [-component=<component>] [-arch=<architecture>] [-release=<release>] [-platform=<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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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/<remoteDir>/<fileName>
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue