go-scm/manifest/compile.go
Claude 2dcb86738a
Some checks failed
Security Scan / security (push) Failing after 8s
Test / test (push) Failing after 22s
chore: migrate to dappco.re vanity import path
Change module path from forge.lthn.ai/core/go-scm to dappco.re/go/core/scm.
Update all Go source imports for migrated packages:
- go-log  -> dappco.re/go/core/log
- go-io   -> dappco.re/go/core/io
- go-i18n -> dappco.re/go/core/i18n
- go-ws   -> dappco.re/go/core/ws
- api     -> dappco.re/go/core/api

Non-migrated packages (cli, config) left on forge.lthn.ai paths.
Replace directives use local paths (../go, ../go-io, etc.) until the
dappco.re vanity URL server resolves these modules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:54:23 +00:00

98 lines
3.1 KiB
Go

package manifest
import (
"crypto/ed25519"
"encoding/json"
"path/filepath"
"time"
coreerr "dappco.re/go/core/log"
"dappco.re/go/core/io"
)
// 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.
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.
func MarshalJSON(cm *CompiledManifest) ([]byte, error) {
return json.MarshalIndent(cm, "", " ")
}
// ParseCompiled decodes a core.json into a CompiledManifest.
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/.
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.
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))
}