Merge branch 'fix/io-migration-build' into new
# Conflicts: # pkg/build/checksum.go # pkg/build/config.go # pkg/build/discovery.go
This commit is contained in:
commit
650fd4d8c8
3 changed files with 74 additions and 64 deletions
|
|
@ -6,25 +6,26 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
io_interface "github.com/host-uk/core/pkg/io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
coreio "github.com/host-uk/core/pkg/io"
|
||||
)
|
||||
|
||||
// Checksum computes SHA256 for an artifact and returns the artifact with the Checksum field filled.
|
||||
func Checksum(fs io_interface.Medium, artifact Artifact) (Artifact, error) {
|
||||
func Checksum(artifact Artifact) (Artifact, error) {
|
||||
if artifact.Path == "" {
|
||||
return Artifact{}, fmt.Errorf("build.Checksum: artifact path is empty")
|
||||
}
|
||||
|
||||
// Open the file
|
||||
file, err := fs.Open(artifact.Path)
|
||||
file, err := os.Open(artifact.Path)
|
||||
if err != nil {
|
||||
return Artifact{}, fmt.Errorf("build.Checksum: failed to open file: %w", err)
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
defer file.Close()
|
||||
|
||||
// Compute SHA256 hash
|
||||
hasher := sha256.New()
|
||||
|
|
@ -44,14 +45,14 @@ func Checksum(fs io_interface.Medium, artifact Artifact) (Artifact, error) {
|
|||
|
||||
// ChecksumAll computes checksums for all artifacts.
|
||||
// Returns a slice of artifacts with their Checksum fields filled.
|
||||
func ChecksumAll(fs io_interface.Medium, artifacts []Artifact) ([]Artifact, error) {
|
||||
func ChecksumAll(artifacts []Artifact) ([]Artifact, error) {
|
||||
if len(artifacts) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var checksummed []Artifact
|
||||
for _, artifact := range artifacts {
|
||||
cs, err := Checksum(fs, artifact)
|
||||
cs, err := Checksum(artifact)
|
||||
if err != nil {
|
||||
return checksummed, fmt.Errorf("build.ChecksumAll: failed to checksum %s: %w", artifact.Path, err)
|
||||
}
|
||||
|
|
@ -68,7 +69,7 @@ func ChecksumAll(fs io_interface.Medium, artifacts []Artifact) ([]Artifact, erro
|
|||
//
|
||||
// The artifacts should have their Checksum fields filled (call ChecksumAll first).
|
||||
// Filenames are relative to the output directory (just the basename).
|
||||
func WriteChecksumFile(fs io_interface.Medium, artifacts []Artifact, path string) error {
|
||||
func WriteChecksumFile(artifacts []Artifact, path string) error {
|
||||
if len(artifacts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -88,8 +89,14 @@ func WriteChecksumFile(fs io_interface.Medium, artifacts []Artifact, path string
|
|||
|
||||
content := strings.Join(lines, "\n") + "\n"
|
||||
|
||||
// Write the file using the medium (which handles directory creation in Write)
|
||||
if err := fs.Write(path, content); err != nil {
|
||||
// Ensure directory exists
|
||||
dir := filepath.Dir(path)
|
||||
if err := coreio.Local.EnsureDir(dir); err != nil {
|
||||
return fmt.Errorf("build.WriteChecksumFile: failed to create directory: %w", err)
|
||||
}
|
||||
|
||||
// Write the file
|
||||
if err := coreio.Local.Write(path, content); err != nil {
|
||||
return fmt.Errorf("build.WriteChecksumFile: failed to write file: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@ package build
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/host-uk/core/pkg/build/signing"
|
||||
"github.com/host-uk/core/pkg/config"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// ConfigFileName is the name of the build configuration file.
|
||||
|
|
@ -21,75 +22,79 @@ const ConfigDir = ".core"
|
|||
// This is distinct from Config which holds runtime build parameters.
|
||||
type BuildConfig struct {
|
||||
// Version is the config file format version.
|
||||
Version int `yaml:"version" mapstructure:"version"`
|
||||
Version int `yaml:"version"`
|
||||
// Project contains project metadata.
|
||||
Project Project `yaml:"project" mapstructure:"project"`
|
||||
Project Project `yaml:"project"`
|
||||
// Build contains build settings.
|
||||
Build Build `yaml:"build" mapstructure:"build"`
|
||||
Build Build `yaml:"build"`
|
||||
// Targets defines the build targets.
|
||||
Targets []TargetConfig `yaml:"targets" mapstructure:"targets"`
|
||||
Targets []TargetConfig `yaml:"targets"`
|
||||
// Sign contains code signing configuration.
|
||||
Sign signing.SignConfig `yaml:"sign,omitempty" mapstructure:"sign,omitempty"`
|
||||
Sign signing.SignConfig `yaml:"sign,omitempty"`
|
||||
}
|
||||
|
||||
// Project holds project metadata.
|
||||
type Project struct {
|
||||
// Name is the project name.
|
||||
Name string `yaml:"name" mapstructure:"name"`
|
||||
Name string `yaml:"name"`
|
||||
// Description is a brief description of the project.
|
||||
Description string `yaml:"description" mapstructure:"description"`
|
||||
Description string `yaml:"description"`
|
||||
// Main is the path to the main package (e.g., ./cmd/core).
|
||||
Main string `yaml:"main" mapstructure:"main"`
|
||||
Main string `yaml:"main"`
|
||||
// Binary is the output binary name.
|
||||
Binary string `yaml:"binary" mapstructure:"binary"`
|
||||
Binary string `yaml:"binary"`
|
||||
}
|
||||
|
||||
// Build holds build-time settings.
|
||||
type Build struct {
|
||||
// CGO enables CGO for the build.
|
||||
CGO bool `yaml:"cgo" mapstructure:"cgo"`
|
||||
CGO bool `yaml:"cgo"`
|
||||
// Flags are additional build flags (e.g., ["-trimpath"]).
|
||||
Flags []string `yaml:"flags" mapstructure:"flags"`
|
||||
Flags []string `yaml:"flags"`
|
||||
// LDFlags are linker flags (e.g., ["-s", "-w"]).
|
||||
LDFlags []string `yaml:"ldflags" mapstructure:"ldflags"`
|
||||
LDFlags []string `yaml:"ldflags"`
|
||||
// Env are additional environment variables.
|
||||
Env []string `yaml:"env" mapstructure:"env"`
|
||||
Env []string `yaml:"env"`
|
||||
}
|
||||
|
||||
// TargetConfig defines a build target in the config file.
|
||||
// This is separate from Target to allow for additional config-specific fields.
|
||||
type TargetConfig struct {
|
||||
// OS is the target operating system (e.g., "linux", "darwin", "windows").
|
||||
OS string `yaml:"os" mapstructure:"os"`
|
||||
OS string `yaml:"os"`
|
||||
// Arch is the target architecture (e.g., "amd64", "arm64").
|
||||
Arch string `yaml:"arch" mapstructure:"arch"`
|
||||
Arch string `yaml:"arch"`
|
||||
}
|
||||
|
||||
// LoadConfig loads build configuration from the .core/build.yaml file in the given directory.
|
||||
// If the config file does not exist, it returns DefaultConfig().
|
||||
// Returns an error if the file exists but cannot be parsed.
|
||||
func LoadConfig(fs io.Medium, dir string) (*BuildConfig, error) {
|
||||
func LoadConfig(dir string) (*BuildConfig, error) {
|
||||
configPath := filepath.Join(dir, ConfigDir, ConfigFileName)
|
||||
|
||||
if !fs.Exists(configPath) {
|
||||
return DefaultConfig(), nil
|
||||
}
|
||||
|
||||
// Use centralized config service
|
||||
c, err := config.New(config.WithMedium(fs), config.WithPath(configPath))
|
||||
// Convert to absolute path for io.Local
|
||||
absPath, err := filepath.Abs(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("build.LoadConfig: %w", err)
|
||||
return nil, fmt.Errorf("build.LoadConfig: failed to resolve path: %w", err)
|
||||
}
|
||||
|
||||
cfg := DefaultConfig()
|
||||
if err := c.Get("", cfg); err != nil {
|
||||
return nil, fmt.Errorf("build.LoadConfig: %w", err)
|
||||
content, err := io.Local.Read(absPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return DefaultConfig(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("build.LoadConfig: failed to read config file: %w", err)
|
||||
}
|
||||
|
||||
// Apply defaults for any missing fields (centralized Get might not fill everything)
|
||||
applyDefaults(cfg)
|
||||
var cfg BuildConfig
|
||||
if err := yaml.Unmarshal([]byte(content), &cfg); err != nil {
|
||||
return nil, fmt.Errorf("build.LoadConfig: failed to parse config file: %w", err)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
// Apply defaults for any missing fields
|
||||
applyDefaults(&cfg)
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// DefaultConfig returns sensible defaults for Go projects.
|
||||
|
|
@ -110,6 +115,7 @@ func DefaultConfig() *BuildConfig {
|
|||
Targets: []TargetConfig{
|
||||
{OS: "linux", Arch: "amd64"},
|
||||
{OS: "linux", Arch: "arm64"},
|
||||
{OS: "darwin", Arch: "amd64"},
|
||||
{OS: "darwin", Arch: "arm64"},
|
||||
{OS: "windows", Arch: "amd64"},
|
||||
},
|
||||
|
|
@ -155,8 +161,8 @@ func ConfigPath(dir string) string {
|
|||
}
|
||||
|
||||
// ConfigExists checks if a build config file exists in the given directory.
|
||||
func ConfigExists(fs io.Medium, dir string) bool {
|
||||
return fs.IsFile(ConfigPath(dir))
|
||||
func ConfigExists(dir string) bool {
|
||||
return fileExists(ConfigPath(dir))
|
||||
}
|
||||
|
||||
// ToTargets converts TargetConfig slice to Target slice for use with builders.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ const (
|
|||
markerWails = "wails.json"
|
||||
markerNodePackage = "package.json"
|
||||
markerComposer = "composer.json"
|
||||
markerCMake = "CMakeLists.txt"
|
||||
)
|
||||
|
||||
// projectMarker maps a marker file to its project type.
|
||||
|
|
@ -29,18 +28,17 @@ var markers = []projectMarker{
|
|||
{markerGoMod, ProjectTypeGo},
|
||||
{markerNodePackage, ProjectTypeNode},
|
||||
{markerComposer, ProjectTypePHP},
|
||||
{markerCMake, ProjectTypeCPP},
|
||||
}
|
||||
|
||||
// Discover detects project types in the given directory by checking for marker files.
|
||||
// Returns a slice of detected project types, ordered by priority (most specific first).
|
||||
// For example, a Wails project returns [wails, go] since it has both wails.json and go.mod.
|
||||
func Discover(fs io.Medium, dir string) ([]ProjectType, error) {
|
||||
func Discover(dir string) ([]ProjectType, error) {
|
||||
var detected []ProjectType
|
||||
|
||||
for _, m := range markers {
|
||||
path := filepath.Join(dir, m.file)
|
||||
if fileExists(fs, path) {
|
||||
if fileExists(path) {
|
||||
// Avoid duplicates (shouldn't happen with current markers, but defensive)
|
||||
if !slices.Contains(detected, m.projectType) {
|
||||
detected = append(detected, m.projectType)
|
||||
|
|
@ -53,8 +51,8 @@ func Discover(fs io.Medium, dir string) ([]ProjectType, error) {
|
|||
|
||||
// PrimaryType returns the most specific project type detected in the directory.
|
||||
// Returns empty string if no project type is detected.
|
||||
func PrimaryType(fs io.Medium, dir string) (ProjectType, error) {
|
||||
types, err := Discover(fs, dir)
|
||||
func PrimaryType(dir string) (ProjectType, error) {
|
||||
types, err := Discover(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -65,32 +63,31 @@ func PrimaryType(fs io.Medium, dir string) (ProjectType, error) {
|
|||
}
|
||||
|
||||
// IsGoProject checks if the directory contains a Go project (go.mod or wails.json).
|
||||
func IsGoProject(fs io.Medium, dir string) bool {
|
||||
return fileExists(fs, filepath.Join(dir, markerGoMod)) ||
|
||||
fileExists(fs, filepath.Join(dir, markerWails))
|
||||
func IsGoProject(dir string) bool {
|
||||
return fileExists(filepath.Join(dir, markerGoMod)) ||
|
||||
fileExists(filepath.Join(dir, markerWails))
|
||||
}
|
||||
|
||||
// IsWailsProject checks if the directory contains a Wails project.
|
||||
func IsWailsProject(fs io.Medium, dir string) bool {
|
||||
return fileExists(fs, filepath.Join(dir, markerWails))
|
||||
func IsWailsProject(dir string) bool {
|
||||
return fileExists(filepath.Join(dir, markerWails))
|
||||
}
|
||||
|
||||
// IsNodeProject checks if the directory contains a Node.js project.
|
||||
func IsNodeProject(fs io.Medium, dir string) bool {
|
||||
return fileExists(fs, filepath.Join(dir, markerNodePackage))
|
||||
func IsNodeProject(dir string) bool {
|
||||
return fileExists(filepath.Join(dir, markerNodePackage))
|
||||
}
|
||||
|
||||
// IsPHPProject checks if the directory contains a PHP project.
|
||||
func IsPHPProject(fs io.Medium, dir string) bool {
|
||||
return fileExists(fs, filepath.Join(dir, markerComposer))
|
||||
}
|
||||
|
||||
// IsCPPProject checks if the directory contains a C++ project.
|
||||
func IsCPPProject(fs io.Medium, dir string) bool {
|
||||
return fileExists(fs, filepath.Join(dir, markerCMake))
|
||||
func IsPHPProject(dir string) bool {
|
||||
return fileExists(filepath.Join(dir, markerComposer))
|
||||
}
|
||||
|
||||
// fileExists checks if a file exists and is not a directory.
|
||||
func fileExists(fs io.Medium, path string) bool {
|
||||
return fs.IsFile(path)
|
||||
func fileExists(path string) bool {
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return io.Local.IsFile(absPath)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue