package main import ( "fmt" "os" "regexp" "strings" "git.oreonproject.org/oreonproject/eternity/common" "git.oreonproject.org/oreonproject/eternity/lib" "os/signal" "path/filepath" "github.com/Masterminds/semver" "github.com/sassoftware/go-rpmutils" ) func main() { if len(os.Args) < 2 { fmt.Println("Usage: eternity ") os.Exit(1) } logger := &common.DefaultLogger for i, arg := range os.Args { if arg == "--help" || arg == "-h" || (i != 1 && arg == "help") { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "To ask for help, use 'eternity help , not --help, -h, or eternity help.", }) } } switch os.Args[1] { case "build": inMemory := true if len(os.Args) > 2 { if os.Args[2] == "-d" || os.Args[2] == "--disk" { inMemory = false } } // Allow the user to choose an output dir var outputDir = "./" // Default output directory // Loop through arguments to find "-o" or "--output" for i := 2; i < len(os.Args); i++ { if os.Args[i] == "-o" || os.Args[i] == "--output" { // Check if next argument exists and is not another flag if i+1 < len(os.Args) && !strings.HasPrefix(os.Args[i+1], "-") { outputDir = os.Args[i+1] if !filepath.IsAbs(outputDir) { outputDir, _ = filepath.Abs(outputDir) // Convert to absolute path if relative } break } else { fmt.Println("FATAL: Output directory not specified or invalid after", os.Args[i]) os.Exit(1) } } } logger.LogFunc(lib.Log{ Level: "INFO", Content: "Build directory set to: " + outputDir, }) privateKey, err, vagueError := common.LoadPrivateKey(logger) if err != nil || vagueError != nil { common.LoadPrivateKeyHandleError(err, vagueError, logger) } config, err, vagueError := lib.ParseConfig("eternity.json", logger) if err != nil || vagueError != nil { common.EternityJsonHandleError(err, vagueError, logger) } else { var tempDir string c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { for sig := range c { fmt.Println("\nCaught", sig, "signal, exiting...") if tempDir != "" { common.BuildEPKRemoveTemporaryDirectory(tempDir, logger) } os.Exit(1) } }() var size int64 size, tempDir, err, vagueError = lib.BuildEPK("./", inMemory, config.Build, logger) if err != nil || vagueError != nil { common.BuildEPKHandleError(tempDir, err, vagueError, logger) } else { filePath := filepath.Join(outputDir, config.Metadata.Name+"-"+config.Metadata.Version.String()) config.Metadata.DecompressedSize = size err, vagueError := lib.PackageEPK(config.Metadata, config.Build, tempDir, filePath+".epk", privateKey, logger) if err != nil || vagueError != nil { common.PackageEPKHandleError(err, vagueError, logger) } else { common.BuildEPKRemoveTemporaryDirectory(tempDir, logger) logger.LogFunc(lib.Log{ Level: "INFO", Content: "Successfully built and packaged EPK at " + filePath + ".epk", }) } } } case "repo": if len(os.Args) < 4 { fmt.Println("Usage: eternity repo ") fmt.Println("See 'eternity help repo' for more information.") os.Exit(1) } privateKey, err, vagueError := common.LoadPrivateKey(logger) if err != nil || vagueError != nil { common.LoadPrivateKeyHandleError(err, vagueError, logger) } switch os.Args[2] { case "generate": err, vagueError := lib.GenerateRepository(os.Args[3], privateKey, logger) if err != nil || vagueError != nil { common.GenerateRepositoryHandleError(err, vagueError, logger) } } case "convert": if len(os.Args) < 4 { fmt.Println("Usage: eternity convert []") os.Exit(1) } privateKey, err, vagueError := common.LoadPrivateKey(logger) if err != nil || vagueError != nil { common.LoadPrivateKeyHandleError(err, vagueError, logger) } rpmFile, err := os.Open(os.Args[2]) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error opening RPM: " + err.Error(), }) } rpm, err := rpmutils.ReadRpm(rpmFile) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error reading RPM: " + err.Error(), }) } name, err := rpm.Header.GetString(rpmutils.NAME) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error getting RPM name: " + err.Error(), }) } description, err := rpm.Header.GetString(rpmutils.DESCRIPTION) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error getting RPM description: " + err.Error(), }) } longDescription := logger.LogFunc(lib.Log{ Level: "INFO", Content: "Enter a long description for the package: ", Prompt: true, }) version, err := rpm.Header.GetString(rpmutils.VERSION) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error getting RPM version: " + err.Error(), }) } release, err := rpm.Header.GetString(rpmutils.RELEASE) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error getting RPM release: " + err.Error(), }) } semanticVersion, err := semver.NewVersion(version + "-" + release) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error parsing RPM version and release: " + err.Error(), }) } author := logger.LogFunc(lib.Log{ Level: "PROMPT", Content: "Enter your preferred author name. This must be the same as the author name used in " + "eternity.json and associated with your keypair, otherwise it will cause issues with EPK" + " verification and your repository will be rejected by Eon and cannot be trusted.", Prompt: true, }) license, err := rpm.Header.GetString(rpmutils.LICENSE) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error getting RPM license: " + err.Error(), }) } decompressedSize, err := rpm.Header.GetUint64s(rpmutils.SIZE) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error getting RPM size: " + err.Error(), }) } dependenciesRpm, err := rpm.Header.GetStrings(rpmutils.REQUIRENAME) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error getting RPM dependencies: " + err.Error(), }) } libRegex := regexp.MustCompile(`^([a-z]+)`) var dependencies []string for _, dependency := range dependenciesRpm { dependencyMatch := libRegex.FindStringSubmatch(dependency) if len(dependencyMatch) > 1 { match := dependencyMatch[1] if match != "rpmlib" && match != "rtld" { var exists bool for _, dep := range dependencies { if dep == match { exists = true } } if !exists { dependencies = append(dependencies, match) } } } } fmt.Println(dependencies) architecture, err := rpm.Header.GetString(rpmutils.ARCH) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error getting RPM architecture: " + err.Error(), }) } metadata := lib.Metadata{ Name: name, Description: description, LongDescription: longDescription, Version: semanticVersion, Author: author, License: license, Architecture: architecture, Dependencies: dependencies, DecompressedSize: int64(decompressedSize[0]), SpecialFiles: lib.SpecialFiles{}, } // Output the RPM data to a temporary directory tempDir, err := os.MkdirTemp("", "eternity-convert-") if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error creating temporary directory: " + err.Error(), }) } err = rpm.ExpandPayload(filepath.Join(tempDir, "payload")) if err != nil { logger.LogFunc(lib.Log{ Level: "FATAL", Content: "Error expanding RPM payload: " + err.Error(), }) } // Package the EPK err, vagueError = lib.PackageEPK(metadata, lib.Build{ TargetRoot: "payload", }, tempDir, os.Args[3], privateKey, logger) if err != nil || vagueError != nil { common.PackageEPKHandleError(err, vagueError, logger) } else { logger.LogFunc(lib.Log{ Level: "INFO", Content: "Successfully converted RPM to EPK at " + os.Args[3], }) } case "help": if len(os.Args) > 2 { switch os.Args[2] { case "build": fmt.Println("Usage: eternity build [-d/--disk]") fmt.Println(" -d, --disk Build EPK on disk instead of in memory. This is useful for large EPKs.") case "convert": fmt.Println("Usage: eternity convert ") case "repo": fmt.Println("Usage: eternity repo ") fmt.Println(" build Build a repository from a directory containing EPK project directories.") fmt.Println(" generate Generate a repository from a directory containing EPKs.") case "package": fmt.Println("Usage: eternity package ") default: fmt.Println("Usage: eternity help ") } } else { fmt.Println("Usage: eternity help ") } } }