Builder/main.go
2025-04-13 19:21:04 +02:00

428 lines
15 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"spitfire-builder/spitfire"
"time"
//"errors"
)
var (
all bool
buildFlag bool
clean bool
fullClean bool
update bool
prePatch bool
postPatch bool
skipPatchUpdate bool
run bool
compress bool
target string
version string
component string
arch string
release string
platform string
upload bool
uploadPath string
sourceRepo = "https://hg.mozilla.org/releases/mozilla-release"
patchesRepo = "https://weforge.xyz/Spitfire/Patcher.git"
url = "https://spitfirebrowser.xyz/"
licence = "AGPL-3.0"
name = "Spitfire"
packageName = "spitfire-browser"
maintainer = "Internet Addict"
initialDir string
skipDeps bool
ignoreErrors bool
)
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")
}
func printHelp() {
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)
}
func handleError(err error) {
if err != nil {
errors = append(errors, err)
if !ignoreErrors {
log.Fatal(err)
}
}
}
func main() {
flag.Parse()
if flag.Lookup("h").Value.(flag.Getter).Get().(bool) {
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))
}
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))
}
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))
} else if buildDir == "" && (buildFlag || all || run) {
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))
}
}
spitfire.PrintErrors()
if len(errors) > 0 {
fmt.Fprintf(os.Stderr, "❌ Exiting with %d error(s):\n", len(errors))
for _, e := range errors {
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)
}
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)
}
if err := spitfire.DiscardChanges(sourcePath); err != nil {
return "", fmt.Errorf("❌ discard changes: %w", err)
}
if err := spitfire.CleanBuild(sourcePath, fullClean); err != nil {
return "", fmt.Errorf("❌ clean build: %w", err)
}
if err := spitfire.UpdateRepo(sourcePath); err != nil {
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)
}
if err := spitfire.Configure(sourcePath); err != nil {
return "", fmt.Errorf("❌ configure: %w", err)
}
if err := spitfire.Build(sourcePath); err != nil {
return "", fmt.Errorf("❌ build: %w", err)
}
buildDir, err = spitfire.ResolveBuildDir(sourcePath)
if err != nil {
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)
}
if run {
if err := spitfire.RunProject(sourcePath); err != nil {
return "", fmt.Errorf("❌ run project: %w", err)
}
}
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)
}
}
if err := spitfire.Configure(sourcePath); err != nil {
return "", fmt.Errorf("❌ configure: %w", err)
}
if err := spitfire.Build(sourcePath); err != nil {
return "", fmt.Errorf("❌ build: %w", err)
}
buildDir, err = spitfire.ResolveBuildDir(sourcePath)
if err != nil {
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)
}
}
if run {
if err := spitfire.RunProject(sourcePath); err != nil {
return "", fmt.Errorf("❌ run project: %w", err)
}
}
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)
}
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)
}
if err := spitfire.UpdateRepo(sourcePath); err != nil {
return "", fmt.Errorf("❌ update repo: %w", err)
}
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)
}
if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil {
return "", fmt.Errorf("❌ pre-patch: %w", err)
}
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)
}
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)
}
}
if run {
if err := spitfire.RunProject(sourcePath); err != nil {
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 buildDir, nil
}
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")
}
fmt.Println("📏 Calculating directory size...")
uncompressedSize, err := spitfire.GetDirectorySize(pathToUse)
if err != nil {
return fmt.Errorf("❌ get directory size: %w", err)
}
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)
}
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)
}
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(" └─ 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)
}
if _, err := os.Stat(outputCompressedFile); err != nil {
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)
}
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 - creating new one")
}
fmt.Println("📝 Updating APPINDEX...")
if err := spitfire.PackageAPPINDEX(
packageName,
release,
version,
arch,
fmt.Sprintf("%d", compressedSize),
fmt.Sprintf("%d", uncompressedSize),
name,
url,
licence,
component,
maintainer,
"",
platform,
uploadDir,
); err != nil {
return fmt.Errorf("❌ package APPINDEX: %w", err)
}
fmt.Println("✅ APPINDEX updated successfully")
fmt.Println("🧹 Cleaning APPINDEX...")
if err := spitfire.CleanAppIndex(); err != nil {
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)
}
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)
}
}