Files
holos/internal/push/instance.go
Jeff McCune 18653534ad (#175) Add holos push platform form command
This sub-command renders the web app form from CUE code and updates the
form using the `holos.platform.v1alpha1.PlatformService/UpdatePlatform`
rpc method.

Example use case, starting fresh:

```
rm -rf ~/holos
mkdir ~/holos
cd ~/holos
```

Step 1: Login

```sh
holos login
```

```txt
9:53AM INF login.go:40 logged in as jeff@ois.run version=0.79.0 name="Jeff McCune" exp="2024-05-17 21:16:07 -0700 PDT" email=jeff@ois.run
```

Step 2: Register to create server side resources.

```sh
holos register user
```

```
9:52AM INF register.go:68 user version=0.79.0 email=jeff@ois.run user_id=018f826d-85a8-751d-81ee-64d0f2775b3f org_id=018f826d-85a8-751e-98dd-a6cddd9dd8f0
```

Step 3: Generate the bare platform in the local filesystem.

```sh
holos generate platform bare
```

```txt
9:52AM INF generate.go:79 wrote platform.metadata.json version=0.79.0 platform_id=018f826d-85a8-751f-96d0-0d2bf70df909 path=/home/jeff/holos/platform.metadata.json
9:52AM INF generate.go:91 generated platform bare version=0.79.0 platform_id=018f826d-85a8-751f-96d0-0d2bf70df909 path=/home/jeff/holos
```

Step 4: Push the platform form to the `holos server` web app.

```sh
holos push platform form .
```

```txt
9:52AM INF client.go:67 updated platform version=0.79.0 platform_id=018f826d-85a8-751f-96d0-0d2bf70df909 duration=73.62995ms
```

At this point the platform form is published and functions as expected
when visiting the platform web interface.
2024-05-17 09:51:36 -07:00

92 lines
2.2 KiB
Go

package push
import (
"context"
"fmt"
"os"
"path/filepath"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/load"
"github.com/holos-run/holos"
"github.com/holos-run/holos/internal/errors"
)
func NewInstance(name string) (*Instance, error) {
absPath, err := filepath.Abs(name)
if err != nil {
return nil, err
}
path := holos.InstancePath(absPath)
mod, err := FindCueMod(path)
if err != nil {
return nil, err
}
return &Instance{path: path, mod: mod}, nil
}
// Instance represents a CUE instance.
type Instance struct {
path holos.InstancePath
mod holos.PathCueMod
}
// Export builds the cue instance into a JSON byte slice. Equivalent of cue
// export.
func (i *Instance) Export(ctx context.Context) ([]byte, error) {
// CUE Loader
cfg := load.Config{Dir: string(i.mod)}
// Make target relative to the module directory
relPath, err := filepath.Rel(string(i.mod), string(i.path))
if err != nil {
return nil, err
}
relPath = "./" + relPath
instances := load.Instances([]string{relPath}, &cfg)
if len(instances) != 1 {
return nil, errors.Wrap(errors.New("exactly one instance is required"))
}
instance := instances[0]
if err := instance.Err; err != nil {
return nil, errors.Wrap(fmt.Errorf("could not load: %w", err))
}
cueCtx := cuecontext.New()
value := cueCtx.BuildInstance(instance)
if err := value.Err(); err != nil {
return nil, errors.Wrap(fmt.Errorf("could not build %s: %w", instance.Dir, err))
}
if err := value.Validate(); err != nil {
return nil, errors.Wrap(fmt.Errorf("could not validate: %w", err))
}
b, err := value.MarshalJSON()
if err != nil {
return nil, errors.Wrap(fmt.Errorf("could not marshal cue instance %s: %w", instance.Dir, err))
}
return b, nil
}
func FindCueMod(name holos.InstancePath) (dir holos.PathCueMod, err error) {
path := holos.PathCueMod(name)
for {
if _, err := os.Stat(filepath.Join(string(path), "cue.mod")); err == nil {
dir = path
break
} else if !os.IsNotExist(err) {
return "", err
}
parentPath := holos.PathCueMod(filepath.Dir(string(path)))
if parentPath == path {
return "", fmt.Errorf("no cue.mod from root to leaf: %v", name)
}
path = parentPath
}
return dir, nil
}