mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 00:37:45 +00:00
Compare commits
4 Commits
jeff/v1alp
...
jeff/compi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f20f2fd203 | ||
|
|
07f0ddd7b2 | ||
|
|
72d465fecc | ||
|
|
1fc2f28289 |
@@ -27,9 +27,9 @@ import core "github.com/holos-run/holos/api/core/v1alpha6"
|
||||
// platform command. Use the Components field to register components with the
|
||||
// platform.
|
||||
type Platform struct {
|
||||
Name string
|
||||
Components map[NameLabel]core.Component
|
||||
Resource core.Platform
|
||||
Name string `json:"name" yaml:"name" cue:"string | *\"default\""`
|
||||
Components map[NameLabel]core.Component `json:"components" yaml:"components"`
|
||||
Resource core.Platform `json:"resource" yaml:"resource"`
|
||||
}
|
||||
|
||||
// ComponentConfig represents the configuration common to all kinds of
|
||||
|
||||
@@ -46,10 +46,10 @@ const ComponentAnnotationsTag = "holos_component_annotations"
|
||||
//
|
||||
// [external credential provider]: https://github.com/kubernetes/enhancements/blob/313ad8b59c80819659e1fbf0f165230f633f2b22/keps/sig-auth/541-external-credential-providers/README.md
|
||||
type BuildPlan struct {
|
||||
// Kind represents the type of the resource.
|
||||
Kind string `json:"kind" yaml:"kind" cue:"\"BuildPlan\""`
|
||||
// APIVersion represents the versioned schema of the resource.
|
||||
APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"\"v1alpha6\""`
|
||||
// Kind represents the type of the resource.
|
||||
Kind string `json:"kind" yaml:"kind" cue:"\"BuildPlan\""`
|
||||
// Metadata represents data about the resource such as the Name.
|
||||
Metadata Metadata `json:"metadata" yaml:"metadata"`
|
||||
// Spec specifies the desired state of the resource.
|
||||
@@ -412,10 +412,10 @@ type Metadata struct {
|
||||
//
|
||||
// cue export --out yaml ./platform
|
||||
type Platform struct {
|
||||
// Kind is a string value representing the resource.
|
||||
Kind string `json:"kind" yaml:"kind" cue:"\"Platform\""`
|
||||
// APIVersion represents the versioned schema of this resource.
|
||||
APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"string | *\"v1alpha6\""`
|
||||
// Kind is a string value representing the resource.
|
||||
Kind string `json:"kind" yaml:"kind" cue:"\"Platform\""`
|
||||
// Metadata represents data about the resource such as the Name.
|
||||
Metadata Metadata `json:"metadata" yaml:"metadata"`
|
||||
|
||||
|
||||
@@ -180,9 +180,9 @@ Platform assembles a core Platform in the Resource field for the holos render pl
|
||||
|
||||
```go
|
||||
type Platform struct {
|
||||
Name string
|
||||
Components map[NameLabel]core.Component
|
||||
Resource core.Platform
|
||||
Name string `json:"name" yaml:"name" cue:"string | *\"default\""`
|
||||
Components map[NameLabel]core.Component `json:"components" yaml:"components"`
|
||||
Resource core.Platform `json:"resource" yaml:"resource"`
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -209,10 +209,10 @@ Holos uses CUE to construct a BuildPlan. Holos injects late binding values such
|
||||
|
||||
```go
|
||||
type BuildPlan struct {
|
||||
// Kind represents the type of the resource.
|
||||
Kind string `json:"kind" yaml:"kind" cue:"\"BuildPlan\""`
|
||||
// APIVersion represents the versioned schema of the resource.
|
||||
APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"\"v1alpha6\""`
|
||||
// Kind represents the type of the resource.
|
||||
Kind string `json:"kind" yaml:"kind" cue:"\"BuildPlan\""`
|
||||
// Metadata represents data about the resource such as the Name.
|
||||
Metadata Metadata `json:"metadata" yaml:"metadata"`
|
||||
// Spec specifies the desired state of the resource.
|
||||
@@ -491,10 +491,10 @@ cue export --out yaml ./platform
|
||||
|
||||
```go
|
||||
type Platform struct {
|
||||
// Kind is a string value representing the resource.
|
||||
Kind string `json:"kind" yaml:"kind" cue:"\"Platform\""`
|
||||
// APIVersion represents the versioned schema of this resource.
|
||||
APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"string | *\"v1alpha6\""`
|
||||
// Kind is a string value representing the resource.
|
||||
Kind string `json:"kind" yaml:"kind" cue:"\"Platform\""`
|
||||
// Metadata represents data about the resource such as the Name.
|
||||
Metadata Metadata `json:"metadata" yaml:"metadata"`
|
||||
|
||||
|
||||
42
go.mod
42
go.mod
@@ -1,28 +1,28 @@
|
||||
module github.com/holos-run/holos
|
||||
|
||||
go 1.23
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.2
|
||||
toolchain go1.23.6
|
||||
|
||||
require (
|
||||
cuelang.org/go v0.12.0
|
||||
cuelang.org/go v0.13.0-alpha.4
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-runewidth v0.0.15
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/princjef/gomarkdoc v1.1.0
|
||||
github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/rogpeppe/go-internal v1.14.1
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/tools v0.29.0
|
||||
golang.org/x/sync v0.13.0
|
||||
golang.org/x/tools v0.32.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
helm.sh/helm/v3 v3.16.3
|
||||
sigs.k8s.io/kustomize/kustomize/v5 v5.5.0
|
||||
)
|
||||
|
||||
require (
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20241125120445-2c00c104c6e1 // indirect
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20250304105642-27e071d2c9b1 // indirect
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
@@ -57,7 +57,7 @@ require (
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/emicklei/proto v1.13.4 // indirect
|
||||
github.com/emicklei/proto v1.14.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
@@ -81,7 +81,7 @@ require (
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240622144329-c177fd99eaa9 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
@@ -128,8 +128,8 @@ require (
|
||||
github.com/nxadm/tail v1.4.11 // indirect
|
||||
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
@@ -140,7 +140,7 @@ require (
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d // indirect
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20250129171521-feedd8250727 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rubenv/sql-migrate v1.7.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
@@ -168,14 +168,14 @@ require (
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/oauth2 v0.25.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/term v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/oauth2 v0.29.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/term v0.31.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/grpc v1.65.0 // indirect
|
||||
|
||||
78
go.sum
78
go.sum
@@ -1,7 +1,7 @@
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20241125120445-2c00c104c6e1 h1:mRwydyTyhtRX2wXS3mqYWzR2qlv6KsmoKXmlz5vInjg=
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20241125120445-2c00c104c6e1/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg=
|
||||
cuelang.org/go v0.12.0 h1:q4W5I+RtDIA27rslQyyt6sWkXX0YS9qm43+U1/3e0kU=
|
||||
cuelang.org/go v0.12.0/go.mod h1:B4+kjvGGQnbkz+GuAv1dq/R308gTkp0sO28FdMrJ2Kw=
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20250304105642-27e071d2c9b1 h1:Dmbd5Q+ENb2C6carvwrMsrOUwJ9X9qfL5JdW32gYAHo=
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20250304105642-27e071d2c9b1/go.mod h1:dqrnoZx62xbOZr11giMPrWbhlaV8euHwciXZEy3baT8=
|
||||
cuelang.org/go v0.13.0-alpha.4 h1:tmMJdmPCh6Y1vCPbvlsrG/g4Un3AnLajTScNDhLGMwU=
|
||||
cuelang.org/go v0.13.0-alpha.4/go.mod h1:kA/8D9/d32IBp2lISWo90BEJhLyS9tqBcvqw2QAEhEg=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
@@ -82,7 +82,7 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
|
||||
@@ -115,8 +115,8 @@ github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug
|
||||
github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emicklei/proto v1.13.4 h1:myn1fyf8t7tAqIzV91Tj9qXpvyXXGXk8OS2H6IBSc9g=
|
||||
github.com/emicklei/proto v1.13.4/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/emicklei/proto v1.14.0 h1:WYxC0OrBuuC+FUCTZvb8+fzEHdZMwLEF+OnVfZA3LXU=
|
||||
github.com/emicklei/proto v1.14.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
|
||||
@@ -206,8 +206,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -362,10 +362,10 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
@@ -404,15 +404,15 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d h1:HWfigq7lB31IeJL8iy7jkUmU/PG1Sr8jVGhS749dbUA=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20250129171521-feedd8250727 h1:A8EM8fVuYc0qbVMw9D6EiKdKTIm1SmLvAWcCc2mipGY=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20250129171521-feedd8250727/go.mod h1:VmWrOlMnBZNtToCWzRlZlIXcJqjo0hS5dwQbRD62gL8=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a h1:w3tdWGKbLGBPtR/8/oO74W6hmz0qE5q0z9aqSAewaaM=
|
||||
github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a/go.mod h1:S8kfXMp+yh77OxPD4fdM6YUknrZpQxLhvxzS4gDHENY=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI=
|
||||
github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
@@ -437,10 +437,10 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
||||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -500,14 +500,14 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -517,18 +517,18 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -558,16 +558,16 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -575,8 +575,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -13,7 +13,7 @@ func New(name string) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: name,
|
||||
Version: version.Version,
|
||||
Version: version.GetVersion(),
|
||||
Args: cobra.NoArgs,
|
||||
CompletionOptions: cobra.CompletionOptions{
|
||||
HiddenDefaultCmd: true,
|
||||
|
||||
27
internal/cli/compile.go
Normal file
27
internal/cli/compile.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/compile"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
//go:embed compile.txt
|
||||
var compileLong string
|
||||
|
||||
// NewCompileCmd returns a new compile command.
|
||||
func NewCompileCmd() *cobra.Command {
|
||||
cmd := command.New("compile")
|
||||
cmd.Short = "Compile Components (stdin) to BuildPlans (stdout) using CUE"
|
||||
cmd.Long = compileLong
|
||||
cmd.Args = cobra.NoArgs
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
c := compile.New()
|
||||
ctx := cmd.Root().Context()
|
||||
return errors.Wrap(c.Run(ctx))
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
17
internal/cli/compile.txt
Normal file
17
internal/cli/compile.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
Reads a stream of JSON-encoded Component objects from standard input. For each
|
||||
Component, builds a CUE instance then exports a BuildPlan. Each resulting
|
||||
BuildPlan is written as a JSON-encoded object to standard output.
|
||||
|
||||
This command encapsulates cue export for concurrent use. It may be used directly
|
||||
by piping the output of holos show platform through jq to select the components
|
||||
list, then to stdin of this command. This command is most often invoked 'holos
|
||||
render platform' to run concurrent cue exports safely.
|
||||
|
||||
For example:
|
||||
|
||||
holos show platform --format=json \
|
||||
| jq '.spec.components[] | {kind: "Component", apiVersion: "v1alpha6", component: .}' \
|
||||
| holos compile --log-level=debug
|
||||
|
||||
Note each platform components element is embedded into the component field of an
|
||||
enveloping object for the purpose of conveying type metadata.
|
||||
@@ -76,6 +76,9 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
// Show
|
||||
rootCmd.AddCommand(NewShowCmd(platform.NewConfig()))
|
||||
|
||||
// Compile
|
||||
rootCmd.AddCommand(NewCompileCmd())
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,15 @@ package cli
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
v1alpha5 "github.com/holos-run/holos/api/core/v1alpha5"
|
||||
v1alpha6 "github.com/holos-run/holos/api/core/v1alpha6"
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/component"
|
||||
"github.com/holos-run/holos/internal/compile"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/platform"
|
||||
@@ -77,37 +82,62 @@ func (s *showBuildPlans) flagSet() *pflag.FlagSet {
|
||||
}
|
||||
|
||||
func (s *showBuildPlans) Run(ctx context.Context, p *platform.Platform) error {
|
||||
encoder, err := holos.NewSequentialEncoder(s.format, s.cfg.Stdout)
|
||||
components := p.Select(s.cfg.ComponentSelectors...)
|
||||
reqs := make([]compile.BuildPlanRequest, len(components))
|
||||
|
||||
for idx, c := range components {
|
||||
tags, err := c.Tags()
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
reqs[idx] = compile.BuildPlanRequest{
|
||||
APIVersion: "v1alpha6",
|
||||
Kind: "BuildPlanRequest",
|
||||
Root: p.Root(),
|
||||
Leaf: c.Path(),
|
||||
WriteTo: s.cfg.WriteTo,
|
||||
TempDir: "${TMPDIR_PLACEHOLDER}",
|
||||
Tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := compile.Compile(ctx, s.cfg.Concurrency, reqs)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
defer encoder.Close()
|
||||
|
||||
opts := platform.BuildOpts{
|
||||
PerComponentFunc: func(ctx context.Context, idx int, pc holos.Component) error {
|
||||
c := component.New(p.Root(), pc.Path(), component.NewConfig())
|
||||
tm, err := c.TypeMeta()
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
opts := holos.NewBuildOpts(p.Root(), pc.Path(), s.cfg.WriteTo, "${TMPDIR_PLACEHOLDER}")
|
||||
|
||||
// TODO(jjm): refactor into [holos.NewBuildOpts] as functional options.
|
||||
// Component name, label, annotations passed via tags to cue.
|
||||
tags, err := pc.Tags()
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
opts.Tags = tags
|
||||
|
||||
bp, err := c.BuildPlan(tm, opts)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// Export the build plan using the sequential encoder.
|
||||
return errors.Wrap(bp.Export(idx, encoder))
|
||||
},
|
||||
encoder, err := holos.NewEncoder(s.format, s.cfg.Stdout)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
return errors.Wrap(p.Build(ctx, opts))
|
||||
for _, buildPlanResponse := range resp {
|
||||
var tm holos.TypeMeta
|
||||
if err := json.Unmarshal(buildPlanResponse.RawMessage, &tm); err != nil {
|
||||
return errors.Format("could not discriminate type meta: %w", err)
|
||||
}
|
||||
if tm.Kind != "BuildPlan" {
|
||||
return errors.Format("invalid kind %s: must be BuildPlan", tm.Kind)
|
||||
}
|
||||
|
||||
var buildPlan any
|
||||
switch tm.APIVersion {
|
||||
case "v1alpha5":
|
||||
buildPlan = &v1alpha5.BuildPlan{}
|
||||
case "v1alpha6":
|
||||
buildPlan = &v1alpha6.BuildPlan{}
|
||||
default:
|
||||
slog.WarnContext(ctx, fmt.Sprintf("unknown BuildPlan APIVersion %s: assuming v1alpha6 schema", tm.APIVersion))
|
||||
buildPlan = &v1alpha6.BuildPlan{}
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(buildPlanResponse.RawMessage, buildPlan); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if err := encoder.Encode(buildPlan); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
250
internal/compile/compile.go
Normal file
250
internal/compile/compile.go
Normal file
@@ -0,0 +1,250 @@
|
||||
// Package compile compiles BuildPlan resources by reading json encoded data
|
||||
// from a reader, unmarshaling the data into a Component, building a CUE
|
||||
// instance injecting the Component as a tag, then exporting a BuildPlan and
|
||||
// marshalling the result to a writer represented as a stream of json objects.
|
||||
// Each input component maps to one output json object in the stream.
|
||||
package compile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
componentPkg "github.com/holos-run/holos/internal/component"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// BuildPlanRequest represents the complete context necessary to produce a
|
||||
// BuildPlan. BuildPlanRequest is the primary input to the holos compile
|
||||
// command, read from standard input. Provided by the holos render platform
|
||||
// command.
|
||||
type BuildPlanRequest struct {
|
||||
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||
Root string `json:"root,omitempty" yaml:"root,omitempty"`
|
||||
Leaf string `json:"leaf,omitempty" yaml:"leaf,omitempty"`
|
||||
WriteTo string `json:"writeTo,omitempty" yaml:"writeTo,omitempty"`
|
||||
TempDir string `json:"tempDir,omitempty" yaml:"tempDir,omitempty"`
|
||||
Tags []string
|
||||
}
|
||||
|
||||
type BuildPlanResponse struct {
|
||||
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||
RawMessage json.RawMessage `json:"rawMessage,omitempty" yaml:"rawMessage,omitempty"`
|
||||
}
|
||||
|
||||
// New returns a new BuildPlan Compiler.
|
||||
func New() *Compiler {
|
||||
return &Compiler{
|
||||
R: os.Stdin,
|
||||
W: os.Stdout,
|
||||
Encoding: "json",
|
||||
}
|
||||
}
|
||||
|
||||
// Compiler reads BuildPlanRequest objects from R and exports
|
||||
// BuildPlanResponse objects to W.
|
||||
type Compiler struct {
|
||||
R io.Reader
|
||||
W io.Writer
|
||||
// Encoding specifies "json" (default) or "yaml" format output.
|
||||
Encoding string
|
||||
}
|
||||
|
||||
// Run reads a BuildPlanRequest from R and compiles a BuildPlan into a
|
||||
// BuildPlanResponse on W. R and W are usually connected to stdin and stdout.
|
||||
func (c *Compiler) Run(ctx context.Context) error {
|
||||
epoch := time.Now()
|
||||
decoder := json.NewDecoder(c.R)
|
||||
encoder, err := holos.NewSequentialEncoder(c.Encoding, c.W)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
slog.DebugContext(ctx, "entering read loop")
|
||||
for idx := 0; ; idx++ {
|
||||
log := slog.With("idx", idx)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Wrap(ctx.Err())
|
||||
default:
|
||||
}
|
||||
|
||||
var raw json.RawMessage
|
||||
err := decoder.Decode(&raw)
|
||||
if err == io.EOF {
|
||||
duration := time.Since(epoch)
|
||||
msg := fmt.Sprintf("received eof: exiting: total runtime %.3fs", duration.Seconds())
|
||||
log.DebugContext(ctx, msg, "eof", true, "seconds", duration.Seconds())
|
||||
return nil
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
var meta holos.TypeMeta
|
||||
err = json.Unmarshal(raw, &meta)
|
||||
if err != nil {
|
||||
return errors.Format("could not unmarshal type meta: %w", err)
|
||||
}
|
||||
log.DebugContext(ctx, fmt.Sprintf("received: %+v", meta), "meta", meta)
|
||||
|
||||
var req BuildPlanRequest
|
||||
|
||||
// platform component from the components field of the platform resource.
|
||||
switch meta.APIVersion {
|
||||
case "v1alpha6":
|
||||
switch meta.Kind {
|
||||
case holos.BuildPlanRequest:
|
||||
err = json.Unmarshal(raw, &req)
|
||||
if err != nil {
|
||||
return errors.Format("could not unmarshal %+v: %w", meta, err)
|
||||
}
|
||||
default:
|
||||
return errors.Format("unsupported kind: %+v (ignored)", meta)
|
||||
}
|
||||
default:
|
||||
return errors.Format("unsupported api version: %+v (ignored)", meta)
|
||||
}
|
||||
|
||||
// Produce the build plan.
|
||||
component := componentPkg.New(req.Root, req.Leaf, componentPkg.NewConfig())
|
||||
tm, err := component.TypeMeta()
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// TODO(jjm): Decide how to handle the temp directory.
|
||||
opts := holos.NewBuildOpts(req.Root, req.Leaf, req.WriteTo, "${TMPDIR_PLACEHOLDER}")
|
||||
|
||||
// Component name, label, annotations passed via tags to cue.
|
||||
opts.Tags = req.Tags
|
||||
|
||||
bp, err := component.BuildPlan(tm, opts)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
if err := bp.Export(idx, encoder); err != nil {
|
||||
return errors.Format("could not marshal output: %w", err)
|
||||
}
|
||||
|
||||
duration := time.Since(start)
|
||||
log.DebugContext(ctx, fmt.Sprintf("compile time: %.3fs", duration.Seconds()))
|
||||
}
|
||||
}
|
||||
|
||||
type task struct {
|
||||
idx int
|
||||
req BuildPlanRequest
|
||||
resp BuildPlanResponse
|
||||
}
|
||||
|
||||
func Compile(ctx context.Context, concurrency int, reqs []BuildPlanRequest) (resp []BuildPlanResponse, err error) {
|
||||
concurrency = min(len(reqs), max(1, concurrency))
|
||||
resp = make([]BuildPlanResponse, len(reqs))
|
||||
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
tasks := make(chan task)
|
||||
|
||||
// Producer
|
||||
g.Go(func() error {
|
||||
for idx, req := range reqs {
|
||||
tsk := task{
|
||||
idx: idx,
|
||||
req: req,
|
||||
resp: BuildPlanResponse{},
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Wrap(ctx.Err())
|
||||
case tasks <- tsk:
|
||||
slog.DebugContext(ctx, fmt.Sprintf("producer producing task seq=%d component=%s tags=%+v", tsk.idx, tsk.req.Leaf, tsk.req.Tags))
|
||||
}
|
||||
}
|
||||
slog.DebugContext(ctx, fmt.Sprintf("producer finished: closing tasks channel"))
|
||||
close(tasks)
|
||||
return nil
|
||||
})
|
||||
|
||||
// Consumers
|
||||
for id := range concurrency {
|
||||
g.Go(func() error {
|
||||
return compiler(ctx, id, tasks, resp)
|
||||
})
|
||||
}
|
||||
|
||||
err = errors.Wrap(g.Wait())
|
||||
return
|
||||
}
|
||||
|
||||
func compiler(ctx context.Context, id int, tasks chan task, resp []BuildPlanResponse) error {
|
||||
log := logger.FromContext(ctx).With("id", id)
|
||||
|
||||
// Start the sub-process
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, exe, "compile")
|
||||
|
||||
stdinPipe, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return errors.Format("could not attach to stdin for worker %d: %w", id, err)
|
||||
}
|
||||
stdoutPipe, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
_ = stdinPipe.Close()
|
||||
return errors.Format("could not attach to stdout for worker %d: %w", id, err)
|
||||
}
|
||||
var stderrBuf bytes.Buffer
|
||||
cmd.Stderr = &stderrBuf
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
_ = stdinPipe.Close()
|
||||
return errors.Format("could not start worker %d: %w", id, err)
|
||||
}
|
||||
pid := cmd.Process.Pid
|
||||
msg := fmt.Sprintf("compiler id=%d pid=%d", id, pid)
|
||||
log.DebugContext(ctx, fmt.Sprintf("%s: started", msg))
|
||||
|
||||
defer func() {
|
||||
stdinPipe.Close()
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.ErrorContext(ctx, fmt.Sprintf("%s: exited uncleanly: %s", msg, err), "err", err, "stderr", stderrBuf.String())
|
||||
}
|
||||
}()
|
||||
|
||||
encoder := json.NewEncoder(stdinPipe)
|
||||
decoder := json.NewDecoder(stdoutPipe)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Wrap(ctx.Err())
|
||||
case tsk, ok := <-tasks:
|
||||
if !ok {
|
||||
log.DebugContext(ctx, fmt.Sprintf("%s: tasks channel closed: returning normally", msg))
|
||||
return nil
|
||||
}
|
||||
log.DebugContext(ctx, fmt.Sprintf("%s: encoding request seq=%d", msg, tsk.idx))
|
||||
if err := encoder.Encode(tsk.req); err != nil {
|
||||
return errors.Format("could not encode request for %s: %w", msg, err)
|
||||
}
|
||||
log.DebugContext(ctx, fmt.Sprintf("%s: decoding response seq=%d", msg, tsk.idx))
|
||||
if err := decoder.Decode(&resp[tsk.idx].RawMessage); err != nil {
|
||||
return errors.Format("could not decode response from %s: %w\n%s", msg, err, stderrBuf.String())
|
||||
}
|
||||
log.DebugContext(ctx, fmt.Sprintf("%s: ok finished task seq=%d", msg, tsk.idx))
|
||||
}
|
||||
}
|
||||
}
|
||||
358
internal/compile/compile_gemini.go
Normal file
358
internal/compile/compile_gemini.go
Normal file
@@ -0,0 +1,358 @@
|
||||
//go:build gemini
|
||||
|
||||
package compile
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime" // To get number of CPUs
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// Placeholder types - replace with your actual definitions
|
||||
type Component struct {
|
||||
ID int `json:"id"` // Add an ID to correlate requests/responses if needed later
|
||||
Name string `json:"name"`
|
||||
Data string `json:"data"`
|
||||
// Add other fields as needed
|
||||
}
|
||||
|
||||
type BuildPlan struct {
|
||||
ComponentID int `json:"componentId"` // Correlates back to Component.ID
|
||||
ComponentUsed string `json:"componentUsed"`
|
||||
Result map[string]interface{} `json:"result"`
|
||||
Success bool `json:"success"`
|
||||
WorkerPID int `json:"workerPid"` // Optional: useful for debugging
|
||||
// Add other fields as needed
|
||||
}
|
||||
|
||||
// executeReusableWorkers starts a pool of worker processes, distributes components
|
||||
// to them, and collects build plans. Uses line-delimited JSON over stdio.
|
||||
func executeReusableWorkers(ctx context.Context, numWorkers int, components []Component) ([]BuildPlan, error) {
|
||||
if len(components) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if numWorkers <= 0 {
|
||||
numWorkers = runtime.NumCPU() // Default to number of CPUs
|
||||
}
|
||||
if numWorkers > len(components) {
|
||||
numWorkers = len(components) // No need for more workers than tasks
|
||||
}
|
||||
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get executable path: %w", err)
|
||||
}
|
||||
|
||||
// Channel for distributing components to worker goroutines
|
||||
// Buffer size == len(components) to avoid blocking the sender initially
|
||||
tasksChan := make(chan Component, len(components))
|
||||
|
||||
// Channel for collecting results from worker goroutines
|
||||
// Buffer size helps prevent worker goroutines blocking if main thread is slow
|
||||
resultsChan := make(chan BuildPlan, len(components))
|
||||
|
||||
// Use an error group to manage worker goroutines and capture the first error
|
||||
g, childCtx := errgroup.WithContext(ctx)
|
||||
|
||||
// Start worker processes and their managing goroutines
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
workerIndex := i // Capture loop variable
|
||||
|
||||
// Create the command for the worker process
|
||||
// Ensure the child knows it's a worker (e.g., via "--worker" flag)
|
||||
cmd := exec.CommandContext(childCtx, exePath, "--worker") // Adapt flag as needed
|
||||
|
||||
stdinPipe, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("worker %d: failed to get stdin pipe: %w", workerIndex, err)
|
||||
}
|
||||
stdoutPipe, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
_ = stdinPipe.Close()
|
||||
return nil, fmt.Errorf("worker %d: failed to get stdout pipe: %w", workerIndex, err)
|
||||
}
|
||||
var stderrBuf bytes.Buffer
|
||||
cmd.Stderr = &stderrBuf
|
||||
|
||||
// Start the worker process
|
||||
if err := cmd.Start(); err != nil {
|
||||
_ = stdinPipe.Close()
|
||||
// stdoutPipe closing is less critical before Wait
|
||||
return nil, fmt.Errorf("worker %d: failed to start command '%s': %w", workerIndex, exePath, err)
|
||||
}
|
||||
pid := cmd.Process.Pid // Get PID for logging/debugging
|
||||
fmt.Printf("Parent: Started worker %d (PID: %d)\n", workerIndex, pid)
|
||||
|
||||
// Launch a goroutine to manage this specific worker
|
||||
g.Go(func() error {
|
||||
// Ensure resources are cleaned up for this worker's goroutine
|
||||
defer func() {
|
||||
fmt.Printf("Parent: Goroutine for worker %d (PID: %d) shutting down stdin.\n", workerIndex, pid)
|
||||
// Closing stdin signals the worker to terminate its loop
|
||||
stdinPipe.Close()
|
||||
// Wait for the process to fully exit after stdin is closed
|
||||
waitErr := cmd.Wait()
|
||||
stderrContent := stderrBuf.String()
|
||||
if waitErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "Parent: Worker %d (PID: %d) exited with error (stderr: %q): %v\n", workerIndex, pid, stderrContent, waitErr)
|
||||
// Note: Returning an error here might race with errors from I/O below, errgroup handles this.
|
||||
} else {
|
||||
fmt.Printf("Parent: Worker %d (PID: %d) exited cleanly.\n", workerIndex, pid)
|
||||
}
|
||||
}()
|
||||
|
||||
// Use buffered I/O for efficiency with line-based protocol
|
||||
writer := bufio.NewWriter(stdinPipe)
|
||||
// Use bufio.Scanner for reading line-delimited output
|
||||
scanner := bufio.NewScanner(stdoutPipe)
|
||||
|
||||
// Process tasks from the channel until it's closed and empty
|
||||
for task := range tasksChan {
|
||||
// Marshal component to JSON
|
||||
jsonData, err := json.Marshal(task)
|
||||
if err != nil {
|
||||
return fmt.Errorf("worker %d (PID: %d): failed to marshal component %d: %w", workerIndex, pid, task.ID, err)
|
||||
}
|
||||
|
||||
// Write JSON line to worker's stdin
|
||||
// fmt.Printf("Parent: Sending task %d to worker %d (PID: %d)\n", task.ID, workerIndex, pid) // Debug logging
|
||||
if _, err := writer.Write(jsonData); err != nil {
|
||||
return fmt.Errorf("worker %d (PID: %d): failed to write component %d to stdin: %w", workerIndex, pid, task.ID, err)
|
||||
}
|
||||
if err := writer.WriteByte('\n'); err != nil { // Write newline delimiter
|
||||
return fmt.Errorf("worker %d (PID: %d): failed to write newline to stdin for component %d: %w", workerIndex, pid, task.ID, err)
|
||||
}
|
||||
if err := writer.Flush(); err != nil { // Ensure data is sent
|
||||
return fmt.Errorf("worker %d (PID: %d): failed to flush stdin for component %d: %w", workerIndex, pid, task.ID, err)
|
||||
}
|
||||
|
||||
// Read line (JSON BuildPlan) from worker's stdout
|
||||
if !scanner.Scan() {
|
||||
// Scanner failed, check for errors or premature EOF
|
||||
if err := scanner.Err(); err != nil {
|
||||
return fmt.Errorf("worker %d (PID: %d): error scanning stdout after sending component %d: %w", workerIndex, pid, task.ID, err)
|
||||
}
|
||||
// If no scanner error, it means EOF was reached unexpectedly
|
||||
return fmt.Errorf("worker %d (PID: %d): unexpected EOF reading stdout after sending component %d", workerIndex, pid, task.ID)
|
||||
}
|
||||
line := scanner.Bytes() // Get the line bytes
|
||||
|
||||
// Unmarshal the BuildPlan
|
||||
var plan BuildPlan
|
||||
if err := json.Unmarshal(line, &plan); err != nil {
|
||||
return fmt.Errorf("worker %d (PID: %d): failed to unmarshal build plan (line: %q): %w", workerIndex, pid, string(line), err)
|
||||
}
|
||||
plan.WorkerPID = pid // Add worker PID for tracking
|
||||
|
||||
// Send the result back to the main goroutine
|
||||
select {
|
||||
case resultsChan <- plan:
|
||||
// fmt.Printf("Parent: Received result for task %d from worker %d (PID: %d)\n", plan.ComponentID, workerIndex, pid) // Debug logging
|
||||
case <-childCtx.Done():
|
||||
return fmt.Errorf("worker %d (PID: %d): context cancelled while sending result for component %d: %w", workerIndex, pid, task.ID, childCtx.Err())
|
||||
}
|
||||
}
|
||||
// tasksChan was closed and this worker processed all its assigned tasks
|
||||
fmt.Printf("Parent: Worker %d (PID: %d) finished processing tasks.\n", workerIndex, pid)
|
||||
return nil // Goroutine finished successfully
|
||||
})
|
||||
}
|
||||
|
||||
// Goroutine to distribute tasks
|
||||
// This runs concurrently with the worker goroutines
|
||||
go func() {
|
||||
fmt.Printf("Parent: Distributing %d tasks...\n", len(components))
|
||||
for i, comp := range components {
|
||||
comp.ID = i // Assign a unique ID for potential correlation
|
||||
select {
|
||||
case tasksChan <- comp:
|
||||
// Task sent
|
||||
case <-childCtx.Done():
|
||||
// Context cancelled before all tasks could be sent
|
||||
fmt.Fprintf(os.Stderr, "Parent: Task distribution cancelled: %v\n", childCtx.Err())
|
||||
// Closing tasksChan here ensures workers eventually stop asking for tasks
|
||||
close(tasksChan)
|
||||
return
|
||||
}
|
||||
}
|
||||
// After sending all tasks, close the channel to signal workers
|
||||
close(tasksChan)
|
||||
fmt.Println("Parent: Finished distributing tasks and closed tasks channel.")
|
||||
}()
|
||||
|
||||
// Wait for all worker goroutines to complete (or one to error out)
|
||||
fmt.Println("Parent: Waiting for workers to finish...")
|
||||
err = g.Wait() // Returns the first error encountered by any worker goroutine
|
||||
|
||||
// Close the results channel *after* all worker goroutines have finished
|
||||
// This signals the final result collection step
|
||||
fmt.Println("Parent: All worker goroutines finished or errored. Closing results channel.")
|
||||
close(resultsChan)
|
||||
|
||||
// Collect all results sent by the workers
|
||||
// This loop reads until resultsChan is closed
|
||||
finalResults := make([]BuildPlan, 0, len(components))
|
||||
for result := range resultsChan {
|
||||
finalResults = append(finalResults, result)
|
||||
}
|
||||
fmt.Printf("Parent: Collected %d results.\n", len(finalResults))
|
||||
|
||||
// Return results even if there was an error, they might be partial
|
||||
if err != nil {
|
||||
// Log the primary error that caused the errgroup to exit
|
||||
fmt.Fprintf(os.Stderr, "Parent: executeReusableWorkers returning with error: %v\n", err)
|
||||
// The finalResults slice might contain results from before the error occurred
|
||||
return finalResults, err
|
||||
}
|
||||
|
||||
// Check if we got the expected number of results (only if no error occurred)
|
||||
if len(finalResults) != len(components) {
|
||||
return finalResults, fmt.Errorf("mismatch: expected %d results, got %d", len(components), len(finalResults))
|
||||
}
|
||||
|
||||
return finalResults, nil // Success
|
||||
}
|
||||
|
||||
// --- Child Worker Logic (Must be added to main.go) ---
|
||||
|
||||
// // Example main function incorporating the worker logic
|
||||
// func main() {
|
||||
// // Check if running as a worker
|
||||
// if len(os.Args) > 1 && os.Args[1] == "--worker" {
|
||||
// // Set GOMAXPROCS? Maybe not necessary if CUE isn't concurrent anyway.
|
||||
// runWorker() // Run the dedicated worker function
|
||||
// return // Important: worker exits via runWorker
|
||||
// }
|
||||
//
|
||||
// // --- Parent Process Logic ---
|
||||
// fmt.Println("Running as parent...")
|
||||
// componentsToProcess := []Component{
|
||||
// {Name: "CompA", Data: "data1"},
|
||||
// {Name: "CompB", Data: "data2"},
|
||||
// {Name: "CompC", Data: "data3"},
|
||||
// {Name: "CompD", Data: "data4"},
|
||||
// {Name: "CompE", Data: "data5"},
|
||||
// }
|
||||
//
|
||||
// ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) // Example timeout
|
||||
// defer cancel()
|
||||
//
|
||||
// // Use, for example, 2 worker processes
|
||||
// buildPlans, err := executeReusableWorkers(ctx, 2, componentsToProcess)
|
||||
// if err != nil {
|
||||
// // Note: buildPlans might contain partial results even if err is non-nil
|
||||
// fmt.Fprintf(os.Stderr, "Error executing reusable workers: %v\n", err)
|
||||
// // Decide if partial results are useful or should be discarded
|
||||
// // os.Exit(1) // Optionally exit
|
||||
// }
|
||||
//
|
||||
// fmt.Printf("Parent: Received %d build plans:\n", len(buildPlans))
|
||||
// // Note: Order is not guaranteed relative to input order.
|
||||
// // Sort or process based on ComponentID if needed.
|
||||
// sort.Slice(buildPlans, func(i, j int) bool {
|
||||
// return buildPlans[i].ComponentID < buildPlans[j].ComponentID
|
||||
// })
|
||||
// for _, plan := range buildPlans {
|
||||
// fmt.Printf(" Plan for Component %d (from worker %d): Success=%t, Result=%v\n",
|
||||
// plan.ComponentID, plan.WorkerPID, plan.Success, plan.Result)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // runWorker implements the logic for a child worker process.
|
||||
// // It reads line-delimited JSON Components from stdin and writes
|
||||
// // line-delimited JSON BuildPlans to stdout.
|
||||
// func runWorker() {
|
||||
// workerPID := os.Getpid()
|
||||
// fmt.Fprintf(os.Stderr, "Worker (PID: %d): Starting\n", workerPID)
|
||||
|
||||
// // Use buffered I/O for stdin and stdout
|
||||
// stdinScanner := bufio.NewScanner(os.Stdin)
|
||||
// stdoutWriter := bufio.NewWriter(os.Stdout)
|
||||
// defer stdoutWriter.Flush() // Ensure buffer is flushed on exit
|
||||
|
||||
// // Loop reading tasks line by line from stdin
|
||||
// for stdinScanner.Scan() {
|
||||
// line := stdinScanner.Bytes()
|
||||
// if len(line) == 0 { // Skip empty lines if any occur
|
||||
// continue
|
||||
// }
|
||||
|
||||
// var comp Component
|
||||
// if err := json.Unmarshal(line, &comp); err != nil {
|
||||
// fmt.Fprintf(os.Stderr, "Worker (PID: %d): Failed to decode component (line: %q): %v\n", workerPID, string(line), err)
|
||||
// // Decide strategy: exit? skip? For now, exit.
|
||||
// os.Exit(1)
|
||||
// }
|
||||
|
||||
// fmt.Fprintf(os.Stderr, "Worker (PID: %d): Processing component %d (%s)\n", workerPID, comp.ID, comp.Name)
|
||||
|
||||
// // --- Simulate CUE processing ---
|
||||
// time.Sleep(time.Duration(500+rand.Intn(500)) * time.Millisecond) // Simulate variable work
|
||||
// success := true
|
||||
// resultData := map[string]interface{}{
|
||||
// "processedData": fmt.Sprintf("Processed %s by %d", comp.Data, workerPID),
|
||||
// "timestamp": time.Now().UnixNano(),
|
||||
// }
|
||||
// // --- End Simulation ---
|
||||
|
||||
// plan := BuildPlan{
|
||||
// ComponentID: comp.ID, // Echo back the ID
|
||||
// ComponentUsed: comp.Name,
|
||||
// Result: resultData,
|
||||
// Success: success,
|
||||
// // WorkerPID added by parent, not needed here
|
||||
// }
|
||||
|
||||
// // Marshal result to JSON
|
||||
// planJSON, err := json.Marshal(plan)
|
||||
// if err != nil {
|
||||
// fmt.Fprintf(os.Stderr, "Worker (PID: %d): Failed to marshal build plan for component %d: %v\n", workerPID, comp.ID, err)
|
||||
// // Decide strategy: exit? skip? For now, exit.
|
||||
// os.Exit(1)
|
||||
// }
|
||||
|
||||
// // Write JSON result line to stdout
|
||||
// if _, err := stdoutWriter.Write(planJSON); err != nil {
|
||||
// fmt.Fprintf(os.Stderr, "Worker (PID: %d): Failed to write build plan for component %d to stdout: %v\n", workerPID, comp.ID, err)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// if err := stdoutWriter.WriteByte('\n'); err != nil { // Add newline delimiter
|
||||
// fmt.Fprintf(os.Stderr, "Worker (PID: %d): Failed to write newline for component %d to stdout: %v\n", workerPID, comp.ID, err)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// // Flush the buffer after each line to ensure parent receives it
|
||||
// if err := stdoutWriter.Flush(); err != nil {
|
||||
// fmt.Fprintf(os.Stderr, "Worker (PID: %d): Failed to flush stdout for component %d: %v\n", workerPID, comp.ID, err)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// fmt.Fprintf(os.Stderr, "Worker (PID: %d): Finished processing component %d (%s)\n", workerPID, comp.ID, comp.Name)
|
||||
// }
|
||||
|
||||
// // Check for scanner errors (e.g., read errors) after the loop finishes
|
||||
// if err := stdinScanner.Err(); err != nil {
|
||||
// // Don't report EOF as an error, it's the signal to exit cleanly
|
||||
// if err != io.EOF {
|
||||
// fmt.Fprintf(os.Stderr, "Worker (PID: %d): Error reading stdin: %v\n", workerPID, err)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // EOF reached on stdin, parent closed the pipe. Exit cleanly.
|
||||
// fmt.Fprintf(os.Stderr, "Worker (PID: %d): Stdin closed, exiting cleanly.\n", workerPID)
|
||||
// os.Exit(0)
|
||||
// }
|
||||
|
||||
// NOTE: Need these imports for the example usage and worker logic:
|
||||
// import (
|
||||
// "io"
|
||||
// "math/rand"
|
||||
// "sort"
|
||||
// "time"
|
||||
// )
|
||||
@@ -37,12 +37,8 @@ func NewConfig() Config {
|
||||
cfg := Config{
|
||||
Concurrency: runtime.NumCPU(),
|
||||
TagMap: make(holos.TagMap),
|
||||
WriteTo: os.Getenv(holos.WriteToEnvVar),
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
if cfg.WriteTo == "" {
|
||||
cfg.WriteTo = holos.WriteToDefault
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,6 @@ func (i *Instance) HolosValue() (v cue.Value, err error) {
|
||||
// Return the deprecated value at the root
|
||||
return i.value, nil
|
||||
}
|
||||
err = errors.Wrap(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ import (
|
||||
// the tests.
|
||||
|
||||
type Component struct {
|
||||
Component core.Component
|
||||
holos.TypeMeta
|
||||
Component core.Component `json:"component,omitempty" yaml:"component,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Component) Describe() string {
|
||||
@@ -802,20 +803,17 @@ func NewBuildContext(opts holos.BuildOpts) (*BuildContext, error) {
|
||||
if !filepath.IsAbs(root) {
|
||||
return nil, errors.Format("not an absolute path: %s", root)
|
||||
}
|
||||
exe, err := util.Executable()
|
||||
holosExecutable, err := util.Executable()
|
||||
if err != nil {
|
||||
return nil, errors.Format("could not get holos path: %w", err)
|
||||
return nil, errors.Format("could not get holos executable path: %w", err)
|
||||
}
|
||||
// Allow placeholder for idempotent holos show buildplan output.
|
||||
tempDir := opts.TempDir()
|
||||
bc := &BuildContext{
|
||||
BuildContext: core.BuildContext{
|
||||
TempDir: tempDir,
|
||||
TempDir: opts.TempDir(),
|
||||
RootDir: root,
|
||||
LeafDir: opts.Leaf(),
|
||||
HolosExecutable: exe,
|
||||
HolosExecutable: holosExecutable,
|
||||
},
|
||||
}
|
||||
|
||||
return bc, nil
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ import "github.com/holos-run/holos/api/core/v1alpha6:core"
|
||||
// platform command. Use the Components field to register components with the
|
||||
// platform.
|
||||
#Platform: {
|
||||
Name: string
|
||||
Components: {[string]: core.#Component} @go(,map[NameLabel]core.Component)
|
||||
Resource: core.#Platform
|
||||
name: string & (string | *"default") @go(Name)
|
||||
components: {[string]: core.#Component} @go(Components,map[NameLabel]core.Component)
|
||||
resource: core.#Platform @go(Resource)
|
||||
}
|
||||
|
||||
// ComponentConfig represents the configuration common to all kinds of
|
||||
|
||||
@@ -34,12 +34,12 @@ package core
|
||||
//
|
||||
// [external credential provider]: https://github.com/kubernetes/enhancements/blob/313ad8b59c80819659e1fbf0f165230f633f2b22/keps/sig-auth/541-external-credential-providers/README.md
|
||||
#BuildPlan: {
|
||||
// Kind represents the type of the resource.
|
||||
kind: string & "BuildPlan" @go(Kind)
|
||||
|
||||
// APIVersion represents the versioned schema of the resource.
|
||||
apiVersion: string & "v1alpha6" @go(APIVersion)
|
||||
|
||||
// Kind represents the type of the resource.
|
||||
kind: string & "BuildPlan" @go(Kind)
|
||||
|
||||
// Metadata represents data about the resource such as the Name.
|
||||
metadata: #Metadata @go(Metadata)
|
||||
|
||||
@@ -432,12 +432,12 @@ package core
|
||||
//
|
||||
// cue export --out yaml ./platform
|
||||
#Platform: {
|
||||
// Kind is a string value representing the resource.
|
||||
kind: string & "Platform" @go(Kind)
|
||||
|
||||
// APIVersion represents the versioned schema of this resource.
|
||||
apiVersion: string & (string | *"v1alpha6") @go(APIVersion)
|
||||
|
||||
// Kind is a string value representing the resource.
|
||||
kind: string & "Platform" @go(Kind)
|
||||
|
||||
// Metadata represents data about the resource such as the Name.
|
||||
metadata: #Metadata @go(Metadata)
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
)
|
||||
|
||||
#Platform: {
|
||||
Name: string | *"no-platform-name"
|
||||
Components: _
|
||||
Resource: core.#Platform & {
|
||||
metadata: name: Name
|
||||
spec: components: [for x in Components {x}]
|
||||
name: _
|
||||
components: _
|
||||
resource: {
|
||||
metadata: "name": name
|
||||
spec: "components": [for x in components {x}]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ package holos
|
||||
|
||||
import "github.com/holos-run/holos/api/author/v1alpha6:author"
|
||||
|
||||
Platform: author.#Platform & {
|
||||
Name: "default"
|
||||
}
|
||||
// holos represents the field holos render platform evaluates, the resource
|
||||
// field of the author.#Platform definition constructed from a components
|
||||
// struct.
|
||||
holos: platform.resource
|
||||
|
||||
// Render a Platform resource for holos to process
|
||||
holos: Platform.Resource
|
||||
platform: author.#Platform
|
||||
|
||||
@@ -12,3 +12,9 @@ const WriteToEnvVar string = "HOLOS_WRITE_TO"
|
||||
// TypeMetaFile represents the file holos uses to discriminate the api version
|
||||
// of a component BuildPlan.
|
||||
const TypeMetaFile string = "typemeta.yaml"
|
||||
|
||||
// BuildPlanRequest represents the kind value of a BuildPlanRequest
|
||||
const BuildPlanRequest string = "BuildPlanRequest"
|
||||
|
||||
// BuildPlanResponse represents the kind value of a BuildPlanResponse
|
||||
const BuildPlanResponse string = "BuildPlanResponse"
|
||||
|
||||
@@ -184,12 +184,14 @@ func (s *Selector) Set(value string) error {
|
||||
// TypeMeta represents the kind and version of a resource holos needs to
|
||||
// process. Useful to discriminate generated resources.
|
||||
type TypeMeta struct {
|
||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||
}
|
||||
|
||||
func NewSequentialEncoder(format string, w io.Writer) (OrderedEncoder, error) {
|
||||
enc, err := NewEncoder(format, w)
|
||||
// NewSequentialEncoder returns a yaml or json encoder that writes to w. The
|
||||
// encoding argument may be "json" or "yaml".
|
||||
func NewSequentialEncoder(encoding string, w io.Writer) (OrderedEncoder, error) {
|
||||
enc, err := NewEncoder(encoding, w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -200,7 +202,8 @@ func NewSequentialEncoder(format string, w io.Writer) (OrderedEncoder, error) {
|
||||
return seqEnc, nil
|
||||
}
|
||||
|
||||
// NewEncoder returns a yaml or json encoder that writes to w.
|
||||
// NewEncoder returns a yaml or json encoder that writes to w. The format
|
||||
// argument specifies "yaml" or "json" format output.
|
||||
func NewEncoder(format string, w io.Writer) (Encoder, error) {
|
||||
switch format {
|
||||
case "yaml":
|
||||
@@ -338,7 +341,7 @@ type BuildOpts struct {
|
||||
// NewBuildOpts returns a [BuildOpts] configured to build the component at leaf
|
||||
// from the platform module at root writing rendered manifests into the deploy
|
||||
// directory.
|
||||
func NewBuildOpts(root, leaf, deploy, tempDir string) BuildOpts {
|
||||
func NewBuildOpts(root, leaf, writeTo, tempDir string) BuildOpts {
|
||||
return BuildOpts{
|
||||
Store: artifact.NewStore(),
|
||||
Concurrency: min(runtime.NumCPU(), 8),
|
||||
@@ -347,7 +350,7 @@ func NewBuildOpts(root, leaf, deploy, tempDir string) BuildOpts {
|
||||
|
||||
root: filepath.Clean(root),
|
||||
leaf: filepath.Clean(leaf),
|
||||
writeTo: filepath.Clean(deploy),
|
||||
writeTo: filepath.Clean(writeTo),
|
||||
tempDir: filepath.Clean(tempDir),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user