2024-08-28 12:10:50 -07:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-09-19 09:13:59 -07:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"regexp"
|
2024-10-28 07:14:05 -07:00
|
|
|
"strings"
|
2024-09-19 09:13:59 -07:00
|
|
|
|
2024-10-02 10:07:12 -07:00
|
|
|
"git.oreonproject.org/oreonproject/eternity/common"
|
|
|
|
"git.oreonproject.org/oreonproject/eternity/lib"
|
2024-09-19 09:13:59 -07:00
|
|
|
|
2024-08-28 12:10:50 -07:00
|
|
|
"os/signal"
|
|
|
|
"path/filepath"
|
2024-09-19 09:13:59 -07:00
|
|
|
|
|
|
|
"github.com/Masterminds/semver"
|
|
|
|
"github.com/sassoftware/go-rpmutils"
|
2024-08-28 12:10:50 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
if len(os.Args) < 2 {
|
|
|
|
fmt.Println("Usage: eternity <build/generate/convert/repo/package>")
|
|
|
|
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 <build/generate/convert/repo/package>, not --help, -h, or eternity <command> 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-28 07:14:05 -07:00
|
|
|
// 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,
|
|
|
|
})
|
|
|
|
|
2024-08-28 12:10:50 -07:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}()
|
2024-10-10 02:25:46 -07:00
|
|
|
var size int64
|
2024-09-01 12:23:37 -07:00
|
|
|
size, tempDir, err, vagueError = lib.BuildEPK("./", inMemory, config.Build, logger)
|
2024-08-28 12:10:50 -07:00
|
|
|
if err != nil || vagueError != nil {
|
|
|
|
common.BuildEPKHandleError(tempDir, err, vagueError, logger)
|
|
|
|
} else {
|
2024-10-28 07:14:05 -07:00
|
|
|
filePath := filepath.Join(outputDir, config.Metadata.Name+"-"+config.Metadata.Version.String())
|
2024-09-01 12:23:37 -07:00
|
|
|
config.Metadata.DecompressedSize = size
|
2024-08-28 12:10:50 -07:00
|
|
|
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",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-09-01 12:23:37 -07:00
|
|
|
case "repo":
|
|
|
|
if len(os.Args) < 4 {
|
|
|
|
fmt.Println("Usage: eternity repo <build/generate> <directory>")
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2024-09-19 09:13:59 -07:00
|
|
|
case "convert":
|
|
|
|
if len(os.Args) < 4 {
|
|
|
|
fmt.Println("Usage: eternity convert </path/to/rpm> [</path/to/output/epk>]")
|
|
|
|
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,
|
2024-10-10 02:25:46 -07:00
|
|
|
Version: semanticVersion,
|
2024-09-19 09:13:59 -07:00
|
|
|
Author: author,
|
|
|
|
License: license,
|
|
|
|
Architecture: architecture,
|
|
|
|
Dependencies: dependencies,
|
2024-10-10 02:25:46 -07:00
|
|
|
DecompressedSize: int64(decompressedSize[0]),
|
2024-09-19 09:13:59 -07:00
|
|
|
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],
|
|
|
|
})
|
|
|
|
}
|
2024-11-02 00:02:50 -07:00
|
|
|
|
|
|
|
case "init":
|
|
|
|
// If it's passed with an argument, make a new folder with that name
|
|
|
|
if len(os.Args) > 2 {
|
|
|
|
if _, err := os.Stat(os.Args[2]); !os.IsNotExist(err) {
|
|
|
|
logger.LogFunc(lib.Log{
|
|
|
|
Level: "FATAL",
|
|
|
|
Content: "Directory " + os.Args[2] + " already exists.",
|
|
|
|
})
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
err := os.MkdirAll(os.Args[2], 0755)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Error creating directory:", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
logger.LogFunc(lib.Log{
|
|
|
|
Level: "INFO",
|
|
|
|
Content: "Created directory " + os.Args[2],
|
|
|
|
})
|
|
|
|
err = common.CreateEternityJson(logger, os.Args[2])
|
|
|
|
if err != nil {
|
|
|
|
logger.LogFunc(lib.Log{
|
|
|
|
Level: "FATAL",
|
|
|
|
Content: "Error creating eternity.json: " + err.Error(),
|
|
|
|
})
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Otherwise, just create a new eternity.json in the current directory
|
|
|
|
dir, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Error getting current directory:", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
err = common.CreateEternityJson(logger, dir)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogFunc(lib.Log{
|
|
|
|
Level: "FATAL",
|
|
|
|
Content: "Error creating eternity.json: " + err.Error(),
|
|
|
|
})
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
2024-08-28 12:10:50 -07:00
|
|
|
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":
|
2024-09-19 09:13:59 -07:00
|
|
|
fmt.Println("Usage: eternity convert </path/to/rpm> </path/to/output/epk>")
|
2024-08-28 12:10:50 -07:00
|
|
|
case "repo":
|
|
|
|
fmt.Println("Usage: eternity repo <build/generate> <directory>")
|
|
|
|
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 <epk>")
|
2024-11-02 00:04:21 -07:00
|
|
|
case "init":
|
|
|
|
fmt.Println("Usage: eternity init [directory]")
|
|
|
|
fmt.Println(" directory Create a new directory with the given name and an eternity.json file. Defaults to the current directory.")
|
2024-08-28 12:10:50 -07:00
|
|
|
default:
|
|
|
|
fmt.Println("Usage: eternity help <build/generate/convert/repo/package>")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fmt.Println("Usage: eternity help <build/generate/convert/repo/package>")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|