Compare commits

...

1 commit
main ... main

3 changed files with 53 additions and 200 deletions

BIN
cmd/cmd

Binary file not shown.

View file

@ -8,7 +8,6 @@ import (
"git.oreonproject.org/oreonproject/eternity/common" "git.oreonproject.org/oreonproject/eternity/common"
"git.oreonproject.org/oreonproject/eternity/lib" "git.oreonproject.org/oreonproject/eternity/lib"
"math/big"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
@ -62,7 +61,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
}() }()
var size big.Int var size int64
size, tempDir, err, vagueError = lib.BuildEPK("./", inMemory, config.Build, logger) size, tempDir, err, vagueError = lib.BuildEPK("./", inMemory, config.Build, logger)
if err != nil || vagueError != nil { if err != nil || vagueError != nil {
common.BuildEPKHandleError(tempDir, err, vagueError, logger) common.BuildEPKHandleError(tempDir, err, vagueError, logger)
@ -238,12 +237,12 @@ func main() {
Name: name, Name: name,
Description: description, Description: description,
LongDescription: longDescription, LongDescription: longDescription,
Version: *semanticVersion, Version: semanticVersion,
Author: author, Author: author,
License: license, License: license,
Architecture: architecture, Architecture: architecture,
Dependencies: dependencies, Dependencies: dependencies,
DecompressedSize: *big.NewInt(int64(decompressedSize[0])), DecompressedSize: int64(decompressedSize[0]),
SpecialFiles: lib.SpecialFiles{}, SpecialFiles: lib.SpecialFiles{},
} }
@ -267,9 +266,6 @@ func main() {
// Package the EPK // Package the EPK
err, vagueError = lib.PackageEPK(metadata, lib.Build{ err, vagueError = lib.PackageEPK(metadata, lib.Build{
TargetRoot: "payload", TargetRoot: "payload",
HooksFolder: lib.PotentiallyNullString{
Null: true,
},
}, tempDir, os.Args[3], privateKey, logger) }, tempDir, os.Args[3], privateKey, logger)
if err != nil || vagueError != nil { if err != nil || vagueError != nil {
common.PackageEPKHandleError(err, vagueError, logger) common.PackageEPKHandleError(err, vagueError, logger)

View file

@ -12,7 +12,6 @@ import (
"crypto/ed25519" "crypto/ed25519"
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"math/big"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -23,45 +22,40 @@ import (
// SpecialFiles is a struct that contains the special files that are not to be deleted or replaced // SpecialFiles is a struct that contains the special files that are not to be deleted or replaced
type SpecialFiles struct { type SpecialFiles struct {
NoDelete []string NoDelete []string `json:"noDelete"`
NoReplace []string NoReplace []string `json:"noReplace"`
} }
// Metadata is a struct that contains the metadata of the package // Metadata is a struct that contains the metadata of the package
type Metadata struct { type Metadata struct {
Name string Name string `json:"name"`
Description string Description string `json:"desc"`
LongDescription string LongDescription string `json:"longDesc"`
Version semver.Version Version *semver.Version
Author string VersionString string `json:"version"`
License string Author string `json:"author"`
Architecture string License string `json:"license"`
Architecture string `json:"arch"`
// The decompressed size may be larger than the int64 allocated for a compressed file // The decompressed size may be larger than the int64 allocated for a compressed file
DecompressedSize big.Int DecompressedSize int64
Dependencies []string Dependencies []string `json:"deps"`
SpecialFiles SpecialFiles SpecialFiles SpecialFiles `json:"specialFiles"`
} }
// Build is a struct that contains the build configuration of the package // Build is a struct that contains the build configuration of the package
type Build struct { type Build struct {
Type string Type string `json:"type"`
Dependencies []string Dependencies []string `json:"deps"`
Steps []string Steps []string `json:"steps"`
TargetRoot string TargetRoot string `json:"root"`
HooksFolder PotentiallyNullString HooksFolder string `json:"hooks"`
FilesFolder PotentiallyNullString FilesFolder string `json:"files"`
}
// PotentiallyNullString is a struct that contains a string that may be null
type PotentiallyNullString struct {
Value string
Null bool
} }
// Config is a struct that contains the configuration of the package // Config is a struct that contains the configuration of the package
type Config struct { type Config struct {
Metadata Metadata Metadata Metadata `json:"metadata"`
Build Build Build Build `json:"build"`
} }
// Log is a struct that contains the log information // Log is a struct that contains the log information
@ -84,22 +78,6 @@ var ErrEternityJsonReadError = errors.New("error reading eternity.json")
var ErrEternityJsonParseError = errors.New("error parsing eternity.json") var ErrEternityJsonParseError = errors.New("error parsing eternity.json")
var ErrEternityJsonMapError = errors.New("error mapping eternity.json") var ErrEternityJsonMapError = errors.New("error mapping eternity.json")
// interfaceToStringSlice converts an interface slice to a string slice
func interfaceToStringSlice(interfaceSlice []interface{}, interfaceName string) ([]string, error) {
// Yes, it's meant to be empty and not nil: JSON arrays are empty, not nil
//goland:noinspection GoPreferNilSlice
stringSlice := []string{}
for _, interfaceValue := range interfaceSlice {
stringValue, ok := interfaceValue.(string)
if !ok {
return nil, errors.New(interfaceName + " are not strings")
}
stringSlice = append(stringSlice, stringValue)
}
return stringSlice, nil
}
// ParseConfig parses the eternity.json file // ParseConfig parses the eternity.json file
func ParseConfig(path string, logger *Logger) (Config, error, error) { func ParseConfig(path string, logger *Logger) (Config, error, error) {
// Open eternity.json // Open eternity.json
@ -113,143 +91,22 @@ func ParseConfig(path string, logger *Logger) (Config, error, error) {
return Config{}, err, ErrEternityJsonOpenError return Config{}, err, ErrEternityJsonOpenError
} }
// Convert the file to a byte buffer
var fileBytes bytes.Buffer
_, err = io.Copy(&fileBytes, file)
if err != nil {
return Config{}, err, ErrEternityJsonReadError
}
// Parse the file as JSON // Parse the file as JSON
var config map[string]interface{} var config Config
err = json.Unmarshal(fileBytes.Bytes(), &config) decoder := json.NewDecoder(file)
err = decoder.Decode(&config)
if err != nil { if err != nil {
return Config{}, err, ErrEternityJsonParseError return Config{}, err, ErrEternityJsonParseError
} }
// Map SpecialFiles // Map the JSON version to a semver version
var parsedSpecialFiles SpecialFiles config.Metadata.Version, err = semver.NewVersion(config.Metadata.VersionString)
specialFiles, ok := config["specialFiles"].(map[string]interface{})
if !ok {
return Config{}, errors.New("specialFiles is not an object"), ErrEternityJsonMapError
}
noDelete, ok := specialFiles["noDelete"].([]interface{})
if !ok {
return Config{}, errors.New("noDelete is not an array"), ErrEternityJsonMapError
}
parsedSpecialFiles.NoDelete, err = interfaceToStringSlice(noDelete, "noDelete")
if err != nil { if err != nil {
return Config{}, err, ErrEternityJsonMapError return Config{}, err, ErrEternityJsonMapError
} }
noReplace, ok := specialFiles["noReplace"].([]interface{})
if !ok {
return Config{}, errors.New("noReplace is not an array"), ErrEternityJsonMapError
}
parsedSpecialFiles.NoReplace, err = interfaceToStringSlice(noReplace, "noReplace")
if err != nil {
return Config{}, err, ErrEternityJsonMapError
}
// Declare the parsedMetadata object
var parsedMetadata Metadata
// Append parsedSpecialFiles to parsedMetadata
parsedMetadata.SpecialFiles = parsedSpecialFiles
// Map the metadata
parsedMetadata.Name, ok = config["name"].(string)
if !ok {
return Config{}, errors.New("name is not a string"), ErrEternityJsonMapError
}
parsedMetadata.Description, ok = config["desc"].(string)
if !ok {
return Config{}, errors.New("description is not a string"), ErrEternityJsonMapError
}
parsedMetadata.LongDescription, ok = config["longDesc"].(string)
if !ok {
return Config{}, errors.New("longDesc is not a string"), ErrEternityJsonMapError
}
versionString, ok := config["version"].(string)
if !ok {
return Config{}, errors.New("version is not a string"), ErrEternityJsonMapError
}
versionPointer, err := semver.NewVersion(versionString)
if err != nil {
return Config{}, err, ErrEternityJsonMapError
}
parsedMetadata.Version = *versionPointer
parsedMetadata.Author, ok = config["author"].(string)
if !ok {
return Config{}, errors.New("author is not a string"), ErrEternityJsonMapError
}
parsedMetadata.License, ok = config["license"].(string)
if !ok {
return Config{}, errors.New("license is not a string"), ErrEternityJsonMapError
}
parsedMetadata.Architecture, ok = config["arch"].(string)
if !ok {
return Config{}, errors.New("arch is not a string"), ErrEternityJsonMapError
}
dependencies, ok := config["deps"].([]interface{})
if !ok {
return Config{}, errors.New("deps is not an array"), ErrEternityJsonMapError
}
parsedMetadata.Dependencies, err = interfaceToStringSlice(dependencies, "dependencies")
if err != nil {
return Config{}, err, ErrEternityJsonMapError
}
// Map build
var parsedBuild Build
build, ok := config["build"].(map[string]interface{})
if !ok {
return Config{}, errors.New("build is not an object"), ErrEternityJsonMapError
}
parsedBuild.Type, ok = build["type"].(string)
if !ok {
return Config{}, errors.New("type is not a string"), ErrEternityJsonMapError
}
buildDependencies, ok := build["deps"].([]interface{})
if !ok {
return Config{}, errors.New("deps is not an array"), ErrEternityJsonMapError
}
parsedBuild.Dependencies, err = interfaceToStringSlice(buildDependencies, "deps")
if err != nil {
return Config{}, err, ErrEternityJsonMapError
}
steps, ok := build["steps"].([]interface{})
if !ok {
return Config{}, errors.New("steps is not an array"), ErrEternityJsonMapError
}
parsedBuild.Steps, err = interfaceToStringSlice(steps, "steps")
if err != nil {
return Config{}, err, ErrEternityJsonMapError
}
parsedBuild.TargetRoot, ok = build["root"].(string)
if !ok {
return Config{}, errors.New("root is not a string"), ErrEternityJsonMapError
}
hooksFolder, ok := build["hooks"].(string)
if !ok {
parsedBuild.HooksFolder = PotentiallyNullString{Null: true}
} else {
parsedBuild.HooksFolder = PotentiallyNullString{Null: false, Value: hooksFolder}
}
filesFolder, ok := build["files"].(string)
if !ok {
parsedBuild.FilesFolder = PotentiallyNullString{Null: true}
} else {
parsedBuild.FilesFolder = PotentiallyNullString{Null: false, Value: filesFolder}
}
// Create the final Config object
parsedConfig := Config{
Metadata: parsedMetadata,
Build: parsedBuild,
}
// Return the final Config object // Return the final Config object
return parsedConfig, nil, nil return config, nil, nil
} }
var ErrBuildEPKTemporaryDirectoryError = errors.New("error creating temporary directory") var ErrBuildEPKTemporaryDirectoryError = errors.New("error creating temporary directory")
@ -265,14 +122,14 @@ var ErrBuildEPKCountingFilesError = errors.New("error counting files")
var ErrBuildEPKBadBuildType = errors.New("bad build type") var ErrBuildEPKBadBuildType = errors.New("bad build type")
// BuildEPK builds the EPK package into a build directory // BuildEPK builds the EPK package into a build directory
func BuildEPK(projectDir string, inMemory bool, buildConfig Build, logger *Logger) (big.Int, string, error, error) { func BuildEPK(projectDir string, inMemory bool, buildConfig Build, logger *Logger) (int64, string, error, error) {
var tempDir string var tempDir string
switch buildConfig.Type { switch buildConfig.Type {
case "chroot": case "chroot":
return *big.NewInt(0), "", nil, ErrBuildEPKChrootError return 0, "", nil, ErrBuildEPKChrootError
case "unrestricted": case "unrestricted":
return *big.NewInt(0), "", nil, ErrBuildEPKUnrestrictedError return 0, "", nil, ErrBuildEPKUnrestrictedError
case "host": case "host":
// Set up the temp dir // Set up the temp dir
var err error var err error
@ -286,25 +143,25 @@ func BuildEPK(projectDir string, inMemory bool, buildConfig Build, logger *Logge
tempDir, err = os.MkdirTemp(projectDir, "eternity-build-") tempDir, err = os.MkdirTemp(projectDir, "eternity-build-")
} }
if err != nil { if err != nil {
return *big.NewInt(0), tempDir, err, ErrBuildEPKTemporaryDirectoryError return 0, tempDir, err, ErrBuildEPKTemporaryDirectoryError
} }
// Copy the hooks folder // Copy the hooks folder
if buildConfig.HooksFolder.Null != true { if buildConfig.HooksFolder != "" {
hooksDir := filepath.Join(projectDir, buildConfig.HooksFolder.Value) hooksDir := filepath.Join(projectDir, buildConfig.HooksFolder)
targetHooksDir := filepath.Join(tempDir, buildConfig.HooksFolder.Value) targetHooksDir := filepath.Join(tempDir, buildConfig.HooksFolder)
logger.LogFunc(Log{ logger.LogFunc(Log{
Level: "INFO", Content: "Copying hooks from " + hooksDir + " to " + targetHooksDir, Prompt: false, Level: "INFO", Content: "Copying hooks from " + hooksDir + " to " + targetHooksDir, Prompt: false,
}) })
err = os.MkdirAll(targetHooksDir, 0755) err = os.MkdirAll(targetHooksDir, 0755)
if err != nil { if err != nil {
return *big.NewInt(0), tempDir, err, ErrBuildEPKCreateHooksError return 0, tempDir, err, ErrBuildEPKCreateHooksError
} }
err = os.CopyFS(targetHooksDir, os.DirFS(hooksDir)) err = os.CopyFS(targetHooksDir, os.DirFS(hooksDir))
if err != nil { if err != nil {
return *big.NewInt(0), tempDir, err, ErrBuildEPKCopyHooksError return 0, tempDir, err, ErrBuildEPKCopyHooksError
} }
} }
@ -321,19 +178,19 @@ func BuildEPK(projectDir string, inMemory bool, buildConfig Build, logger *Logge
file, err := os.OpenFile(tempDir+"/build.sh", os.O_CREATE|os.O_RDWR, 0755) file, err := os.OpenFile(tempDir+"/build.sh", os.O_CREATE|os.O_RDWR, 0755)
if err != nil { if err != nil {
return *big.NewInt(0), tempDir, err, ErrBuildEPKBuildShError return 0, tempDir, err, ErrBuildEPKBuildShError
} }
_, err = file.WriteString(shellScript) _, err = file.WriteString(shellScript)
if err != nil { if err != nil {
return *big.NewInt(0), tempDir, err, ErrBuildEPKWritingBuildShError return 0, tempDir, err, ErrBuildEPKWritingBuildShError
} }
// Set up the target root // Set up the target root
targetRoot := filepath.Join(tempDir, buildConfig.TargetRoot) targetRoot := filepath.Join(tempDir, buildConfig.TargetRoot)
err = os.MkdirAll(targetRoot, 0755) err = os.MkdirAll(targetRoot, 0755)
if err != nil { if err != nil {
return *big.NewInt(0), tempDir, err, ErrBuildEPKTargetRootError return 0, tempDir, err, ErrBuildEPKTargetRootError
} }
// Execute the shell script in BWrap // Execute the shell script in BWrap
@ -365,10 +222,10 @@ func BuildEPK(projectDir string, inMemory bool, buildConfig Build, logger *Logge
"/bin/sh", "/eternity/build.sh", "/bin/sh", "/eternity/build.sh",
} }
if buildConfig.FilesFolder.Null != true { if buildConfig.FilesFolder != "" {
arguments = arguments[:len(arguments)-4] arguments = arguments[:len(arguments)-4]
arguments = append( arguments = append(
arguments, "--bind", filepath.Join(projectDir, buildConfig.FilesFolder.Value), filepath.Join("/", buildConfig.FilesFolder.Value), arguments, "--bind", filepath.Join(projectDir, buildConfig.FilesFolder), filepath.Join("/", buildConfig.FilesFolder),
"/usr/bin/fakeroot-tcp", "--", "/usr/bin/fakeroot-tcp", "--",
"/bin/sh", "/eternity/build.sh", "/bin/sh", "/eternity/build.sh",
) )
@ -380,12 +237,12 @@ func BuildEPK(projectDir string, inMemory bool, buildConfig Build, logger *Logge
} }
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
return *big.NewInt(0), tempDir, err, ErrBuildEPKExecutingBuildShError return 0, tempDir, err, ErrBuildEPKExecutingBuildShError
} }
// Hopefully, the build was successful. Let's give the user a file and size count. // Hopefully, the build was successful. Let's give the user a file and size count.
var fileCount int var fileCount int
var sizeCount big.Int var sizeCount int64
// We start at -1 because the root directory is not counted // We start at -1 because the root directory is not counted
dirCount := -1 dirCount := -1
err = filepath.Walk(targetRoot, func(path string, info os.FileInfo, err error) error { err = filepath.Walk(targetRoot, func(path string, info os.FileInfo, err error) error {
@ -395,23 +252,23 @@ func BuildEPK(projectDir string, inMemory bool, buildConfig Build, logger *Logge
fileCount++ fileCount++
} }
// Both directories and files need to have their sizes counted // Both directories and files need to have their sizes counted
sizeCount.Add(&sizeCount, big.NewInt(info.Size())) sizeCount += info.Size()
return nil return nil
}) })
if err != nil { if err != nil {
return *big.NewInt(0), tempDir, err, ErrBuildEPKCountingFilesError return 0, tempDir, err, ErrBuildEPKCountingFilesError
} }
logger.LogFunc(Log{ logger.LogFunc(Log{
Level: "INFO", Level: "INFO",
Content: "Build successful. " + strconv.Itoa(fileCount) + " files and " + strconv.Itoa(dirCount) + Content: "Build successful. " + strconv.Itoa(fileCount) + " files and " + strconv.Itoa(dirCount) +
" directories created," + " totalling " + sizeCount.String() + " bytes.", " directories created," + " totalling " + strconv.FormatInt(sizeCount, 10) + " bytes.",
Prompt: false, Prompt: false,
}) })
return sizeCount, tempDir, nil, nil return sizeCount, tempDir, nil, nil
default: default:
return *big.NewInt(0), "", errors.New(buildConfig.Type), ErrBuildEPKBadBuildType return 0, "", errors.New(buildConfig.Type), ErrBuildEPKBadBuildType
} }
} }
@ -539,8 +396,8 @@ func PackageEPK(metaData Metadata, build Build, tempDir string, output string, p
return err, ErrPackageEPKMoveToDistError return err, ErrPackageEPKMoveToDistError
} }
if build.HooksFolder.Null != true { if build.HooksFolder != "" {
hooksDir := filepath.Join(tempDir, build.HooksFolder.Value) hooksDir := filepath.Join(tempDir, build.HooksFolder)
err = os.Rename(hooksDir, distDir+"/hooks") err = os.Rename(hooksDir, distDir+"/hooks")
if err != nil { if err != nil {
return err, ErrPackageEPKMoveToDistError return err, ErrPackageEPKMoveToDistError
@ -565,7 +422,7 @@ func PackageEPK(metaData Metadata, build Build, tempDir string, output string, p
"noDelete": metaData.SpecialFiles.NoDelete, "noDelete": metaData.SpecialFiles.NoDelete,
"noReplace": metaData.SpecialFiles.NoReplace, "noReplace": metaData.SpecialFiles.NoReplace,
}, },
"size": metaData.DecompressedSize.String(), "size": metaData.DecompressedSize,
} }
// Make the data template into a JSON string // Make the data template into a JSON string