dodo: Support Sketch agent
Change-Id: I4dcd6aab7d7a2c2e86aaf1ad8d36d30a649ab31d
diff --git a/apps/app-runner/Dockerfile.sketch.latest b/apps/app-runner/Dockerfile.sketch.latest
new file mode 100644
index 0000000..82aba7a
--- /dev/null
+++ b/apps/app-runner/Dockerfile.sketch.latest
@@ -0,0 +1,9 @@
+FROM alpine:3.22.0
+
+ARG TARGETARCH
+
+RUN apk update
+RUN apk add bash curl git nodejs npm
+
+COPY --from=giolekva/sketch:latest /usr/bin/sketch /usr/bin/sketch
+COPY app-runner_${TARGETARCH} /usr/bin/app-runner
diff --git a/apps/app-runner/Makefile b/apps/app-runner/Makefile
index 187ca18..ef1c483 100644
--- a/apps/app-runner/Makefile
+++ b/apps/app-runner/Makefile
@@ -11,6 +11,7 @@
manifest_dest_deno_2_2_0=docker://docker.io/$(repo_name)/app-runner:deno-2.2.0
manifest_dest_nodejs_23_1_0=docker://docker.io/$(repo_name)/app-runner:nodejs-23.1.0
manifest_dest_nodejs_24_0_2=docker://docker.io/$(repo_name)/app-runner:nodejs-24.0.2
+manifest_dest_sketch_latest=docker://docker.io/$(repo_name)/app-runner:sketch-latest
endif
clean:
@@ -166,6 +167,24 @@
$(podman) manifest push $(repo_name)/app-runner:deno-2.2.0 $(manifest_dest_deno_2_2_0)
$(podman) manifest rm $(repo_name)/app-runner:deno-2.2.0
+# Sketch
+
+push_sketch_arm64: clean build_arm64
+ $(podman) build --platform linux/arm64 --tag=$(repo_name)/app-runner:sketch-latest-arm64 -f Dockerfile.sketch.latest $(docker_flags) .
+ $(podman) push $(repo_name)/app-runner:sketch-latest-arm64
+
+push_sketch_amd64: clean build_amd64
+ $(podman) build --platform linux/amd64 --tag=$(repo_name)/app-runner:sketch-latest-amd64 -f Dockerfile.sketch.latest $(docker_flags) .
+ $(podman) push $(repo_name)/app-runner:sketch-latest-amd64
+
+push_sketch:
+ make -C ../../../sketch push
+ make push_sketch_arm64
+ make push_sketch_amd64
+ $(podman) manifest create $(repo_name)/app-runner:sketch-latest $(repo_name)/app-runner:sketch-latest-arm64 $(repo_name)/app-runner:sketch-latest-amd64
+ $(podman) manifest push $(repo_name)/app-runner:sketch-latest $(manifest_dest_sketch_latest)
+ $(podman) manifest rm $(repo_name)/app-runner:sketch-latest
+
# all
-push: push_golang_1_22_0 push_golang_1_20_0 push_hugo push_php_8_2_apache push_nextjs_deno_2_0_0 push_nodejs_23_1_0 push_nodejs_24_0_2 push_deno_2_2_0
+push: push_golang_1_22_0 push_golang_1_20_0 push_hugo push_php_8_2_apache push_nextjs_deno_2_0_0 push_nodejs_23_1_0 push_nodejs_24_0_2 push_deno_2_2_0 push_sketch
diff --git a/apps/app-runner/main.go b/apps/app-runner/main.go
index 96a647e..ebd0ef8 100644
--- a/apps/app-runner/main.go
+++ b/apps/app-runner/main.go
@@ -21,6 +21,7 @@
var port = flag.Int("port", 3000, "Port to listen on")
var appId = flag.String("app-id", "", "Application ID")
var service = flag.String("service", "", "Service name")
+var agentMode = flag.Bool("agent-mode", false, "Sketch agent mode")
var repoAddr = flag.String("repo-addr", "", "Git repository address")
var branch = flag.String("branch", "", "Name of the branch to process")
var rootDir = flag.String("root-dir", "/", "Path to the app code")
@@ -104,8 +105,10 @@
panic(err)
}
}
- if err := os.Mkdir(*appDir, os.ModePerm); err != nil {
- panic(err)
+ if !*agentMode {
+ if err := os.Mkdir(*appDir, os.ModePerm); err != nil {
+ panic(err)
+ }
}
r, err := os.Open(*runCfg)
if err != nil {
@@ -116,7 +119,7 @@
if err := json.NewDecoder(r).Decode(&cmds); err != nil {
panic(err)
}
- s := NewServer(*port, *appId, *service, id, *repoAddr, *branch, *rootDir, signer, *appDir, cmds, self, *managerAddr)
+ s := NewServer(*agentMode, *port, *appId, *service, id, *repoAddr, *branch, *rootDir, signer, *appDir, cmds, self, *managerAddr)
if err := s.Start(); err != nil {
log.Fatal(err)
}
diff --git a/apps/app-runner/server.go b/apps/app-runner/server.go
index 1f7fe01..89f621a 100644
--- a/apps/app-runner/server.go
+++ b/apps/app-runner/server.go
@@ -31,6 +31,7 @@
type Server struct {
l sync.Locker
+ agentMode bool
port int
appId string
service string
@@ -50,9 +51,10 @@
status *Status
}
-func NewServer(port int, appId, service, id, repoAddr, branch, rootDir string, signer ssh.Signer, appDir string, runCommands []Command, self string, manager string) *Server {
+func NewServer(agentMode bool, port int, appId, service, id, repoAddr, branch, rootDir string, signer ssh.Signer, appDir string, runCommands []Command, self string, manager string) *Server {
return &Server{
l: &sync.Mutex{},
+ agentMode: agentMode,
port: port,
ready: false,
appId: appId,
@@ -114,30 +116,80 @@
s.l.Unlock()
}
+type command struct {
+ cmd string
+ env []string
+}
+
func (s *Server) run() error {
- newDir, err := os.MkdirTemp(s.appDir, "code-*")
- if err != nil {
- return err
- }
- commit, err := CloneRepositoryBranch(s.repoAddr, s.branch, s.rootDir, s.signer, newDir)
- if err != nil {
- fmt.Fprintf(s.logs, "!!! dodo: Failed to clone repository\n")
- s.status = &Status{
- Commit: nil,
+ newDir := s.appDir
+ commands := []command{}
+ if !s.agentMode {
+ var err error
+ newDir, err = os.MkdirTemp(s.appDir, "code-*")
+ if err != nil {
+ return err
}
- return err
}
- fmt.Fprintf(s.logs, "!!! dodo: Successfully cloned repository %s\n", commit.Hash)
- s.status = &Status{
- Commit: commit,
- Commands: []CommandStatus{},
+ if s.repoAddr != "" {
+ commit, err := CloneRepositoryBranch(s.repoAddr, s.branch, s.rootDir, s.signer, newDir)
+ if err != nil {
+ fmt.Fprintf(s.logs, "!!! dodo: Failed to clone repository\n")
+ s.status = &Status{
+ Commit: nil,
+ }
+ return err
+ }
+ fmt.Fprintf(s.logs, "!!! dodo: Successfully cloned repository %s\n", commit.Hash)
+ s.status = &Status{
+ Commit: commit,
+ Commands: []CommandStatus{},
+ }
+ } else {
+ s.status = &Status{
+ Commit: nil,
+ Commands: []CommandStatus{},
+ }
}
- commands := []string{}
+ if s.agentMode {
+ if _, err := os.Stat(filepath.Join(newDir, ".git")); err != nil && os.IsNotExist(err) {
+ commands = append(commands, command{cmd: "git config --global user.name dodo"})
+ s.status.Commands = append(s.status.Commands, CommandStatus{
+ Command: commands[len(commands)-1].cmd,
+ State: "waiting",
+ })
+ commands = append(commands, command{cmd: "git config --global user.email dodo@dodo.cloud"})
+ s.status.Commands = append(s.status.Commands, CommandStatus{
+ Command: commands[len(commands)-1].cmd,
+ State: "waiting",
+ })
+ commands = append(commands, command{cmd: "git init ."})
+ s.status.Commands = append(s.status.Commands, CommandStatus{
+ Command: commands[len(commands)-1].cmd,
+ State: "waiting",
+ })
+ commands = append(commands, command{cmd: "echo \"TODO: Describe project\" > README.md"})
+ s.status.Commands = append(s.status.Commands, CommandStatus{
+ Command: commands[len(commands)-1].cmd,
+ State: "waiting",
+ })
+ commands = append(commands, command{cmd: "git add README.md"})
+ s.status.Commands = append(s.status.Commands, CommandStatus{
+ Command: commands[len(commands)-1].cmd,
+ State: "waiting",
+ })
+ commands = append(commands, command{cmd: "git commit -m \"init\""})
+ s.status.Commands = append(s.status.Commands, CommandStatus{
+ Command: commands[len(commands)-1].cmd,
+ State: "waiting",
+ })
+ }
+ }
for _, c := range s.runCommands {
args := []string{c.Bin}
args = append(args, c.Args...)
cmd := strings.Join(args, " ")
- commands = append(commands, cmd)
+ commands = append(commands, command{cmd, c.Env})
s.status.Commands = append(s.status.Commands, CommandStatus{
Command: cmd,
State: "waiting",
@@ -151,8 +203,8 @@
cmd := &exec.Cmd{
Dir: filepath.Join(newDir, s.rootDir),
Path: "/bin/sh",
- Args: []string{"/bin/sh", "-c", c},
- Env: append(os.Environ(), s.runCommands[i].Env...),
+ Args: []string{"/bin/sh", "-c", c.cmd},
+ Env: append(os.Environ(), c.env...),
Stdout: logM,
Stderr: logM,
}
@@ -160,7 +212,7 @@
fmt.Printf("Running: %s\n", c)
fmt.Fprintf(s.logs, "!!! dodo: Running: %s\n", c)
s.status.Commands[i].State = "running"
- if i < len(s.runCommands)-1 {
+ if i < len(commands)-1 {
if err := cmd.Run(); err != nil {
return err
}
@@ -170,7 +222,7 @@
if err := s.kill(); err != nil {
return err
}
- if s.currDir != "" {
+ if s.currDir != "" && !s.agentMode {
if err := os.RemoveAll(s.currDir); err != nil {
return err
}
diff --git a/charts/app-runner/templates/install.yaml b/charts/app-runner/templates/install.yaml
index 52b8495..8bc558b 100644
--- a/charts/app-runner/templates/install.yaml
+++ b/charts/app-runner/templates/install.yaml
@@ -108,6 +108,7 @@
fieldPath: status.podIP
command:
- app-runner
+ - --agent-mode={{ .Values.agentMode }}
- --port={{ .Values.apiPort }}
- --app-id={{ .Values.appId }}
- --service={{ .Values.name }}
diff --git a/charts/app-runner/values.yaml b/charts/app-runner/values.yaml
index db623a9..5fa1e89 100644
--- a/charts/app-runner/values.yaml
+++ b/charts/app-runner/values.yaml
@@ -16,3 +16,4 @@
extraContainers: []
apiPort: 3000
name: "app"
+agentMode: false
diff --git a/core/installer/app_configs/dodo_app.cue b/core/installer/app_configs/dodo_app.cue
index e906f43..c26fa77 100644
--- a/core/installer/app_configs/dodo_app.cue
+++ b/core/installer/app_configs/dodo_app.cue
@@ -30,6 +30,11 @@
cluster: clusterMap[strings.ToLower(_cluster)]
}
+ geminiApiKey?: string
+ for v in agent {
+ "sketch_\(v.name)_session_id": string @role(sketch-session-id)
+ }
+
for v in _postgresql {
for i, e in v.expose {
"port_postgresql_\(v.name)_\(i)": int @role(port)
@@ -135,26 +140,30 @@
}
// TODO(gio): add value
+// Do not specify both alias and value
#EnvVar: {
name: string
alias?: string
+ value?: string
}
#AppTmpl: {
- name: string | *"app"
- type: string
+ nodeId?: string
+ name: string | *"app"
+ type: string
+ agentMode: bool | *false
ingress?: [...#AppIngress]
expose: [...#PortDomain] | *[]
rootDir: string
runConfiguration: [...#Command]
volume: [...string] | *[]
- dev: #Dev
- vm: #VMCustomization
+ dev: #Dev | *{enabled: false}
+ vm: #VMCustomization
// TODO(gio): check for duplicate values
apiPort: #PortValue | *3000
ports: [...#Port]
env: [...#EnvVar] | *[]
- source: close({
+ source?: close({
repository: string
branch: string | *"master"
rootDir: string | *"/"
@@ -184,6 +193,11 @@
}
},
],
+ [
+ for e in env if e.value != _|_ {
+ "\(strings.ToUpper(e.name))=\(e.value)"
+ },
+ ],
])
...
@@ -467,9 +481,73 @@
#NodeJSApp: #NodeJS2310 | #NodeJS2402
-#App: #GoApp | #HugoApp | #PHPApp | #NextjsApp | #NodeJSApp | #DenoApp
+#SketchApp: #AppTmpl & {
+ agentMode: true
-service: [...#App]
+ name: string
+ _name: name
+
+ type: "sketch:latest"
+
+ geminiApiKey?: string
+ _geminiApiKey: string
+ if geminiApiKey != _|_ {
+ _geminiApiKey: geminiApiKey
+ }
+ if geminiApiKey == _|_ && input.geminiApiKey != _|_ {
+ _geminiApiKey: input.geminiApiKey
+ }
+
+ ports: [{
+ name: "agent"
+ value: 2001
+ }, {
+ name: "p8080"
+ value: 8080
+ }, {
+ name: "p8081"
+ value: 8081
+ }, {
+ name: "p8082"
+ value: 8082
+ }, {
+ name: "p8083"
+ value: 8083
+ }, {
+ name: "p8084"
+ value: 8084
+ }]
+ env: [{
+ name: "DODO_PROJECT_ID"
+ value: input.appId
+ }, {
+ name: "DODO_API_BASE_ADDR"
+ value: input.managerAddr
+ }, {
+ name: "GEMINI_API_KEY"
+ value: _geminiApiKey
+ }]
+ rootDir: "/dodo/volume/\(_name)-apps"
+
+ lastCmdEnv: [...string]
+
+ _sessionId: input["sketch_\(name)_session_id"]
+
+ runConfiguration: [{
+ bin: "sketch -verbose -unsafe -skaband-addr=\"\" -addr=\"0.0.0.0:2001\" -model=gemini -session-id=\"\(_sessionId)\""
+ env: lastCmdEnv
+ }]
+}
+
+#AgentApp: #SketchApp
+
+#NonAgentApp: #GoApp | #HugoApp | #PHPApp | #NextjsApp | #NodeJSApp | #DenoApp
+
+#App: #NonAgentApp | #AgentApp
+
+agent: [...#AgentApp]
+
+service: [...#NonAgentApp]
_serviceDevEnabled: {
images: {}
@@ -617,11 +695,19 @@
containerPort: p.value
protocol: p.protocol
}]
- appDir: svc.rootDir
- appId: input.appId
- repoAddr: svc.source.repository
- branch: svc.source.branch
- rootDir: svc.source.rootDir
+ appDir: svc.rootDir
+ appId: input.appId
+ if svc.source != _|_ {
+ repoAddr: svc.source.repository
+ branch: svc.source.branch
+ rootDir: svc.source.rootDir
+ }
+ if svc.source == _|_ {
+ repoAddr: ""
+ branch: ""
+ rootDir: ""
+ }
+ agentMode: svc.agentMode
sshPrivateKey: base64.Encode(null, input.key.private)
runCfg: base64.Encode(null, json.Marshal(svc.runConfiguration))
managerAddr: input.managerAddr
@@ -730,8 +816,9 @@
}]
runCmd: list.Concat([[
["sh", "-c", "chown \(username):\(username) /home/\(username)/.cache"],
- ["sh", "-c", "GIT_SSH_COMMAND='ssh -i /home/\(username)/.ssh/key -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new' git clone --branch \(svc.source.branch) \(svc.source.repository) /home/\(username)/code"],
- ["sh", "-c", "chown -R \(username):\(username) /home/\(username)/code"],
+ if svc.source != _|_ {
+ ["sh", "-c", "GIT_SSH_COMMAND='ssh -i /home/\(username)/.ssh/key -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new' git clone --branch \(svc.source.branch) \(svc.source.repository) /home/\(username)/code"]
+ },
["sh", "-c", "chown -R \(username):\(username) /home/\(username)"],
], svc.vm.cloudInit.runCmd])
}
@@ -820,6 +907,12 @@
for v in _volume {
"\(v.name)": v
}
+ for v in agent {
+ "\(v.name)-apps": {
+ name: "\(v.name)-apps"
+ size: "1Gi"
+ }
+ }
}
postgresql: {
for v in _postgresql {
@@ -842,6 +935,15 @@
svc: v
}
}
+ for v in agent {
+ "\(v.name)": #Service & {
+ name: v.name
+ svc: v & {
+ dev: enabled: false
+ volume: ["\(v.name)-apps"]
+ }
+ }
+ }
}
}
diff --git a/core/installer/app_manager.go b/core/installer/app_manager.go
index 0965507..eee4841 100644
--- a/core/installer/app_manager.go
+++ b/core/installer/app_manager.go
@@ -1298,6 +1298,8 @@
return []string{}
case KindPassword:
return []string{}
+ case KindSketchSessionId:
+ return []string{}
default:
panic("MUST NOT REACH!")
}
diff --git a/core/installer/derived.go b/core/installer/derived.go
index 4030623..eebb8ad 100644
--- a/core/installer/derived.go
+++ b/core/installer/derived.go
@@ -3,8 +3,10 @@
import (
"fmt"
"html/template"
+ "math/rand/v2"
"strings"
+ "github.com/richardlehane/crock32"
"github.com/sethvargo/go-password/password"
)
@@ -101,6 +103,9 @@
}
ret[k] = psswd
}
+ if def.Kind() == KindSketchSessionId {
+ ret[k] = GenerateSketchSessionId()
+ }
if def.Kind() == KindVPNAuthKey {
enabled := true
if v, ok := def.Meta()["enabledField"]; ok {
@@ -146,6 +151,8 @@
ret[k] = v
case KindPassword:
ret[k] = v
+ case KindSketchSessionId:
+ ret[k] = v
case KindArrayString:
a, ok := v.([]string)
if !ok {
@@ -352,3 +359,12 @@
func GeneratePassword() (string, error) {
return password.Generate(20, 5, 0, false, true)
}
+
+func GenerateSketchSessionId() string {
+ u1, u2 := rand.Uint64(), rand.Uint64N(1<<16)
+ s := crock32.Encode(u1) + crock32.Encode(uint64(u2))
+ if len(s) < 16 {
+ s += strings.Repeat("0", 16-len(s))
+ }
+ return s[0:4] + "-" + s[4:8] + "-" + s[8:12] + "-" + s[12:16]
+}
diff --git a/core/installer/dodo_app_test.go b/core/installer/dodo_app_test.go
index 22835b6..f81f2f9 100644
--- a/core/installer/dodo_app_test.go
+++ b/core/installer/dodo_app_test.go
@@ -642,3 +642,78 @@
}
t.Log(string(access))
}
+
+const sketch = `
+{
+ "agent": [
+ {
+ "type": "sketch:latest",
+ "name": "dev",
+ "geminiApiKey": "foo",
+ }
+ ],
+}
+`
+
+func TestSketch(t *testing.T) {
+ app, err := NewDodoApp([]byte(sketch))
+ if err != nil {
+ for _, e := range errors.Errors(err) {
+ t.Log(e)
+ }
+ t.Fatal(err)
+ }
+ release := Release{
+ Namespace: "foo",
+ AppInstanceId: "foo-bar",
+ RepoAddr: "ssh://192.168.100.210:22/config",
+ AppDir: "/foo/bar",
+ }
+ keyGen := testKeyGen{}
+ r, err := app.Render(release, env, networks, nil, map[string]any{
+ "managerAddr": "",
+ "appId": "",
+ "geminiApiKey": "dev",
+ }, nil, keyGen)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(string(r.Raw))
+}
+
+const sketchGlobalGeminiApiKey = `
+{
+ "agent": [
+ {
+ "type": "sketch:latest",
+ "name": "dev",
+ }
+ ],
+}
+`
+
+func TestSketchGlobalGeminiApiKey(t *testing.T) {
+ app, err := NewDodoApp([]byte(sketchGlobalGeminiApiKey))
+ if err != nil {
+ for _, e := range errors.Errors(err) {
+ t.Log(e)
+ }
+ t.Fatal(err)
+ }
+ release := Release{
+ Namespace: "foo",
+ AppInstanceId: "foo-bar",
+ RepoAddr: "ssh://192.168.100.210:22/config",
+ AppDir: "/foo/bar",
+ }
+ keyGen := testKeyGen{}
+ r, err := app.Render(release, env, networks, nil, map[string]any{
+ "managerAddr": "",
+ "appId": "",
+ "geminiApiKey": "dev",
+ }, nil, keyGen)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(string(r.Raw))
+}
diff --git a/core/installer/go.mod b/core/installer/go.mod
index b183a46..eb35fb5 100644
--- a/core/installer/go.mod
+++ b/core/installer/go.mod
@@ -19,6 +19,7 @@
github.com/libdns/libdns v0.2.2
github.com/miekg/dns v1.1.58
github.com/ncruces/go-sqlite3 v0.17.0
+ github.com/richardlehane/crock32 v1.0.1
github.com/sethvargo/go-password v0.3.1
github.com/spf13/cobra v1.8.1
golang.org/x/crypto v0.32.0
diff --git a/core/installer/go.sum b/core/installer/go.sum
index 1da1bc6..a6448e0 100644
--- a/core/installer/go.sum
+++ b/core/installer/go.sum
@@ -363,6 +363,8 @@
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
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/richardlehane/crock32 v1.0.1 h1:GV9EqtAr7RminQ8oGrDt3gYXkzDDPJ5fROaO1Mux14g=
+github.com/richardlehane/crock32 v1.0.1/go.mod h1:xUIlLABtHBgs1bNIBdUQR9F2xtRzS0TujtbR68hmEWU=
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=
diff --git a/core/installer/samples/canvas.rest b/core/installer/samples/canvas.rest
index 1ff7356..42bc95d 100644
--- a/core/installer/samples/canvas.rest
+++ b/core/installer/samples/canvas.rest
@@ -1,54 +1,24 @@
-PUT http://appmanager.hgrz-appmanager.svc.cluster.local/api/dodo-app/dodo-app-gry
+POST http://appmanager.hgrz-appmanager.svc.cluster.local/api/dodo-app
Content-Type: application/json
{
"config": {
- "service": [
+ "input": {
+ "sketch_dev_gemini_api_key": "AIzaSyAx_vF0HJyT55A09iXtjPhf2JocNOGaWCo"
+ },
+ "agent": [
{
- "dev": {
- "enabled": false
- },
+ "name": "dev",
"ingress": [
{
- "auth": {
- "enabled": true,
- "noAuthPathPatterns": ["^/api/webhook/github/push$"]
- },
- "network": "public",
+ "network": "private",
"port": {
- "name": "web"
+ "value": 2001
},
- "subdomain": "canvas"
+ "subdomain": "sketch"
}
- ],
- "name": "canvas",
- "ports": [
- {
- "name": "web",
- "value": 8080
- },
- {
- "name": "api",
- "value": 8081
- }
- ],
- "source": {
- "branch": "canvas",
- "repository": "https://code.v1.dodo.cloud/pcloud",
- "rootDir": "apps/canvas/back"
- },
- "type": "nodejs:24.0.2",
- "volume": ["data"],
- "preBuildCommands": [{
- "bin": "cd ../front && npm install && npm run build"
- }, {
- "bin": "npx prisma migrate dev"
- }]
+ ]
}
- ],
- "volume": [{
- "name": "data",
- "size": "1Gi"
- }]
+ ]
}
}
diff --git a/core/installer/schema.go b/core/installer/schema.go
index 2b150a6..e575aa5 100644
--- a/core/installer/schema.go
+++ b/core/installer/schema.go
@@ -12,20 +12,21 @@
type Kind int
const (
- KindBoolean Kind = 0
- KindInt = 7
- KindString = 1
- KindStruct = 2
- KindNetwork = 3
- KindMultiNetwork = 10
- KindAuth = 5
- KindSSHKey = 6
- KindNumber = 4
- KindArrayString = 8
- KindPort = 9
- KindVPNAuthKey = 11
- KindCluster = 12
- KindPassword = 13
+ KindBoolean Kind = 0
+ KindInt = 7
+ KindString = 1
+ KindStruct = 2
+ KindNetwork = 3
+ KindMultiNetwork = 10
+ KindAuth = 5
+ KindSSHKey = 6
+ KindNumber = 4
+ KindArrayString = 8
+ KindPort = 9
+ KindVPNAuthKey = 11
+ KindCluster = 12
+ KindPassword = 13
+ KindSketchSessionId = 14
)
type Field struct {
@@ -309,6 +310,8 @@
if role == "password" {
// TODO(gio): implement configurable requirements such as min-length, ...
return basicSchema{name, KindPassword, false, nil}, nil
+ } else if role == "sketch-session-id" {
+ return basicSchema{name, KindSketchSessionId, false, nil}, nil
} else if role == "vpnauthkey" {
meta := map[string]string{}
usernameFieldAttr := v.Attribute("usernameField")