eternity-rs/cmd/main.go
2024-10-02 18:07:12 +01:00

303 lines
8.4 KiB
Go

package main
import (
"fmt"
"os"
"regexp"
"git.oreonproject.org/oreonproject/eternity/common"
"git.oreonproject.org/oreonproject/eternity/lib"
"math/big"
"os/signal"
"path/filepath"
"github.com/Masterminds/semver"
"github.com/sassoftware/go-rpmutils"
)
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
}
}
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 big.Int
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("./", 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 <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)
}
}
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,
Version: *semanticVersion,
Author: author,
License: license,
Architecture: architecture,
Dependencies: dependencies,
DecompressedSize: *big.NewInt(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",
HooksFolder: lib.PotentiallyNullString{
Null: true,
},
}, 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 </path/to/rpm> </path/to/output/epk>")
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>")
default:
fmt.Println("Usage: eternity help <build/generate/convert/repo/package>")
}
} else {
fmt.Println("Usage: eternity help <build/generate/convert/repo/package>")
}
}
}