105 lines
3.4 KiB
Go
105 lines
3.4 KiB
Go
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
package manifest
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
filepath "dappco.re/go/core/scm/internal/ax/filepathx"
|
|
json "dappco.re/go/core/scm/internal/ax/jsonx"
|
|
"time"
|
|
|
|
"dappco.re/go/core/io"
|
|
coreerr "dappco.re/go/core/log"
|
|
)
|
|
|
|
// CompiledManifest is the distribution-ready form of a manifest, written as
|
|
// core.json at the repository root (not inside .core/). It embeds the
|
|
// original Manifest and adds build metadata stapled at compile time.
|
|
type CompiledManifest struct {
|
|
Manifest `json:",inline" yaml:",inline"`
|
|
|
|
// Build metadata — populated by Compile.
|
|
Commit string `json:"commit,omitempty" yaml:"commit,omitempty"`
|
|
Tag string `json:"tag,omitempty" yaml:"tag,omitempty"`
|
|
BuiltAt string `json:"built_at,omitempty" yaml:"built_at,omitempty"`
|
|
BuiltBy string `json:"built_by,omitempty" yaml:"built_by,omitempty"`
|
|
}
|
|
|
|
// CompileOptions controls how Compile populates the build metadata.
|
|
type CompileOptions struct {
|
|
Commit string // Git commit hash
|
|
Tag string // Git tag (e.g. v1.0.0)
|
|
BuiltBy string // Builder identity (e.g. "core build")
|
|
SignKey ed25519.PrivateKey // Optional — signs before compiling
|
|
}
|
|
|
|
// Compile produces a CompiledManifest from a source manifest and build
|
|
// options. If opts.SignKey is provided the manifest is signed first.
|
|
// Usage: Compile(...)
|
|
func Compile(m *Manifest, opts CompileOptions) (*CompiledManifest, error) {
|
|
if m == nil {
|
|
return nil, coreerr.E("manifest.Compile", "nil manifest", nil)
|
|
}
|
|
if m.Code == "" {
|
|
return nil, coreerr.E("manifest.Compile", "missing code", nil)
|
|
}
|
|
if m.Version == "" {
|
|
return nil, coreerr.E("manifest.Compile", "missing version", nil)
|
|
}
|
|
|
|
// Sign if a key is supplied.
|
|
if opts.SignKey != nil {
|
|
if err := Sign(m, opts.SignKey); err != nil {
|
|
return nil, coreerr.E("manifest.Compile", "sign failed", err)
|
|
}
|
|
}
|
|
|
|
return &CompiledManifest{
|
|
Manifest: *m,
|
|
Commit: opts.Commit,
|
|
Tag: opts.Tag,
|
|
BuiltAt: time.Now().UTC().Format(time.RFC3339),
|
|
BuiltBy: opts.BuiltBy,
|
|
}, nil
|
|
}
|
|
|
|
// MarshalJSON serialises a CompiledManifest to JSON bytes.
|
|
// Usage: MarshalJSON(...)
|
|
func MarshalJSON(cm *CompiledManifest) ([]byte, error) {
|
|
return json.MarshalIndent(cm, "", " ")
|
|
}
|
|
|
|
// ParseCompiled decodes a core.json into a CompiledManifest.
|
|
// Usage: ParseCompiled(...)
|
|
func ParseCompiled(data []byte) (*CompiledManifest, error) {
|
|
var cm CompiledManifest
|
|
if err := json.Unmarshal(data, &cm); err != nil {
|
|
return nil, coreerr.E("manifest.ParseCompiled", "unmarshal failed", err)
|
|
}
|
|
return &cm, nil
|
|
}
|
|
|
|
const compiledPath = "core.json"
|
|
|
|
// WriteCompiled writes a CompiledManifest as core.json to the given root
|
|
// directory. The file lives at the distribution root, not inside .core/.
|
|
// Usage: WriteCompiled(...)
|
|
func WriteCompiled(medium io.Medium, root string, cm *CompiledManifest) error {
|
|
data, err := MarshalJSON(cm)
|
|
if err != nil {
|
|
return coreerr.E("manifest.WriteCompiled", "marshal failed", err)
|
|
}
|
|
path := filepath.Join(root, compiledPath)
|
|
return medium.Write(path, string(data))
|
|
}
|
|
|
|
// LoadCompiled reads and parses a core.json from the given root directory.
|
|
// Usage: LoadCompiled(...)
|
|
func LoadCompiled(medium io.Medium, root string) (*CompiledManifest, error) {
|
|
path := filepath.Join(root, compiledPath)
|
|
data, err := medium.Read(path)
|
|
if err != nil {
|
|
return nil, coreerr.E("manifest.LoadCompiled", "read failed", err)
|
|
}
|
|
return ParseCompiled([]byte(data))
|
|
}
|