Merge pull request #504 from samalba/inputs-test

Inputs tests + description from cue doc string
This commit is contained in:
Andrea Luzzardi 2021-05-26 11:12:42 -07:00 committed by GitHub
commit 5d63b22ad1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 258 additions and 180 deletions

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"strings"
"text/tabwriter" "text/tabwriter"
"go.dagger.io/dagger/client" "go.dagger.io/dagger/client"
@ -49,7 +50,7 @@ var listCmd = &cobra.Command{
inputs := env.ScanInputs(ctx) inputs := env.ScanInputs(ctx)
w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
fmt.Fprintln(w, "Input\tType\tValue\tSet by user") fmt.Fprintln(w, "Input\tType\tValue\tSet by user\tDescription")
for _, inp := range inputs { for _, inp := range inputs {
isConcrete := (inp.IsConcreteR() == nil) isConcrete := (inp.IsConcreteR() == nil)
@ -69,11 +70,12 @@ var listCmd = &cobra.Command{
} }
} }
fmt.Fprintf(w, "%s\t%s\t%s\t%t\n", fmt.Fprintf(w, "%s\t%s\t%s\t%t\t%s\n",
inp.Path(), inp.Path(),
getType(inp), getType(inp),
valStr, valStr,
isUserSet(st, inp), isUserSet(st, inp),
getDocString(inp),
) )
} }
@ -108,6 +110,34 @@ func getType(val *compiler.Value) string {
return val.Cue().IncompleteKind().String() return val.Cue().IncompleteKind().String()
} }
func getDocString(val *compiler.Value) string {
docs := []string{}
for _, c := range val.Cue().Doc() {
docs = append(docs, strings.TrimSpace(c.Text()))
}
doc := strings.Join(docs, " ")
lines := strings.Split(doc, "\n")
// Strip out FIXME, TODO, and INTERNAL comments
docs = []string{}
for _, line := range lines {
if strings.HasPrefix(line, "FIXME: ") ||
strings.HasPrefix(line, "TODO: ") ||
strings.HasPrefix(line, "INTERNAL: ") {
continue
}
if len(line) == 0 {
continue
}
docs = append(docs, line)
}
if len(docs) == 0 {
return "-"
}
return strings.Join(docs, " ")
}
func init() { func init() {
listCmd.Flags().BoolP("all", "a", false, "List all inputs (include non-overridable)") listCmd.Flags().BoolP("all", "a", false, "List all inputs (include non-overridable)")

View File

@ -14,6 +14,7 @@ awsConfig: aws.#Config & {
// Name of the S3 bucket to use // Name of the S3 bucket to use
bucket: *"dagger-io-examples" | string @dagger(input) bucket: *"dagger-io-examples" | string @dagger(input)
// Source code to deploy
source: dagger.#Artifact @dagger(input) source: dagger.#Artifact @dagger(input)
url: "\(deploy.url)index.html" url: "\(deploy.url)index.html"

View File

@ -14,22 +14,24 @@ import (
config: aws.#Config config: aws.#Config
// Source is the Cloudformation template (JSON/YAML string) // Source is the Cloudformation template (JSON/YAML string)
source: string source: string @dagger(input)
// Stackname is the cloudformation stack // Stackname is the cloudformation stack
stackName: string stackName: string @dagger(input)
// Stack parameters // Stack parameters
parameters: [string]: _ parameters: {
...
}
// Behavior when failure to create/update the Stack // Behavior when failure to create/update the Stack
onFailure: *"DO_NOTHING" | "ROLLBACK" | "DELETE" onFailure: *"DO_NOTHING" | "ROLLBACK" | "DELETE" @dagger(input)
// Timeout for waiting for the stack to be created/updated (in minutes) // Timeout for waiting for the stack to be created/updated (in minutes)
timeout: *10 | uint timeout: *10 | uint @dagger(input)
// Never update the stack if already exists // Never update the stack if already exists
neverUpdate: *false | bool neverUpdate: *false | bool @dagger(input)
#files: { #files: {
"/entrypoint.sh": #Code "/entrypoint.sh": #Code
@ -44,7 +46,9 @@ import (
} }
} }
outputs: [string]: string outputs: {
[string]: string @dagger(output)
}
outputs: #up: [ outputs: #up: [
op.#Load & { op.#Load & {

View File

@ -15,7 +15,7 @@ import (
// ECR credentials // ECR credentials
username: "AWS" username: "AWS"
secret: out secret: out @dagger(output)
aws.#Script & { aws.#Script & {
always: true always: true

View File

@ -11,22 +11,24 @@ import (
config: aws.#Config config: aws.#Config
// ECS cluster name // ECS cluster name
cluster: string cluster: string @dagger(input)
// Arn of the task to run // Arn of the task to run
taskArn: string taskArn: string @dagger(input)
// Environment variables of the task // Environment variables of the task
containerEnvironment: [string]: string containerEnvironment: {
[string]: string @dagger(input)
}
// Container name // Container name
containerName: string containerName: string @dagger(input)
// Container command to give // Container command to give
containerCommand: [...string] containerCommand: [...string] @dagger(input)
// Task role ARN // Task role ARN
roleArn: string | *"" roleArn: string | *"" @dagger(input)
containerOverrides: { containerOverrides: {
containerOverrides: [{ containerOverrides: [{

View File

@ -11,10 +11,10 @@ import (
config: aws.#Config config: aws.#Config
// EKS cluster name // EKS cluster name
clusterName: string clusterName: string @dagger(input)
// Kubectl version // Kubectl version
version: *"v1.19.9" | string version: *"v1.19.9" | string @dagger(input)
// kubeconfig is the generated kube configuration file // kubeconfig is the generated kube configuration file
kubeconfig: { kubeconfig: {
@ -62,5 +62,5 @@ import (
format: "string" format: "string"
}, },
] ]
} } @dagger(output)
} }

View File

@ -10,12 +10,14 @@ import (
config: aws.#Config config: aws.#Config
// ListenerArn // ListenerArn
listenerArn: string listenerArn: string @dagger(input)
// Optional vhost for reusing priorities // Optional vhost for reusing priorities
vhost?: string vhost?: string @dagger(input)
// exported priority // exported priority
priority: out @dagger(output)
out: string out: string
aws.#Script & { aws.#Script & {

View File

@ -11,18 +11,18 @@ import (
config: aws.#Config config: aws.#Config
// DB name // DB name
name: string name: string @dagger(input)
// ARN of the database instance // ARN of the database instance
dbArn: string dbArn: string @dagger(input)
// ARN of the database secret (for connecting via rds api) // ARN of the database secret (for connecting via rds api)
secretArn: string secretArn: string @dagger(input)
dbType: "mysql" | "postgres" dbType: "mysql" | "postgres" @dagger(input)
// Name of the DB created // Name of the DB created
out: string out: string @dagger(output)
aws.#Script & { aws.#Script & {
"config": config "config": config

View File

@ -7,7 +7,7 @@ import (
// Build a Docker image from source, using included Dockerfile // Build a Docker image from source, using included Dockerfile
#Build: { #Build: {
source: dagger.#Artifact source: dagger.#Artifact @dagger(input)
#up: [ #up: [
op.#DockerBuild & { op.#DockerBuild & {
@ -20,7 +20,7 @@ import (
// Pull a docker container // Pull a docker container
#Pull: { #Pull: {
// Remote ref (example: "index.docker.io/alpine:latest") // Remote ref (example: "index.docker.io/alpine:latest")
from: string from: string @dagger(input)
#up: [ #up: [
op.#FetchContainer & {ref: from}, op.#FetchContainer & {ref: from},
@ -30,10 +30,10 @@ import (
// Push a docker image // Push a docker image
#Push: { #Push: {
// Remote ref (example: "index.docker.io/alpine:latest") // Remote ref (example: "index.docker.io/alpine:latest")
ref: string ref: string @dagger(input)
// Image // Image
source: dagger.#Artifact source: dagger.#Artifact @dagger(input)
#up: [ #up: [
op.#Load & {from: source}, op.#Load & {from: source},
@ -46,8 +46,8 @@ import (
// Build a Docker image from the provided Dockerfile contents // Build a Docker image from the provided Dockerfile contents
// FIXME: incorporate into #Build // FIXME: incorporate into #Build
#ImageFromDockerfile: { #ImageFromDockerfile: {
dockerfile: string dockerfile: string @dagger(input)
context: dagger.#Artifact context: dagger.#Artifact @dagger(input)
#up: [ #up: [
op.#DockerBuild & { op.#DockerBuild & {

View File

@ -7,9 +7,9 @@ import (
) )
#Create: { #Create: {
filename: !="" filename: !="" @dagger(input)
permissions: int | *0o644 permissions: int | *0o644 @dagger(input)
contents: string | bytes contents: string | bytes @dagger(input)
#up: [ #up: [
op.#WriteFile & {dest: filename, content: contents, mode: permissions}, op.#WriteFile & {dest: filename, content: contents, mode: permissions},
@ -17,12 +17,12 @@ import (
} }
#Append: { #Append: {
filename: !="" filename: !="" @dagger(input)
permissions: int | *0o644 permissions: int | *0o644 @dagger(input)
contents: string | bytes contents: string | bytes @dagger(input)
from: dagger.#Artifact from: dagger.#Artifact @dagger(input)
orig: (#read & {path: filename, "from": from}).data orig: (#read & {path: filename, "from": from}).data @dagger(output)
#up: [ #up: [
op.#WriteFile & {dest: filename, content: "\(orig)\(contents)", mode: permissions}, op.#WriteFile & {dest: filename, content: "\(orig)\(contents)", mode: permissions},
@ -30,30 +30,30 @@ import (
} }
#Read: { #Read: {
filename: !="" filename: !="" @dagger(input)
from: dagger.#Artifact from: dagger.#Artifact @dagger(input)
contents: (#read & {path: filename, "from": from}).data contents: (#read & {path: filename, "from": from}).data @dagger(output)
} }
#read: { #read: {
path: !="" path: !="" @dagger(input)
from: dagger.#Artifact from: dagger.#Artifact @dagger(input)
data: { data: {
string string
#up: [ #up: [
op.#Load & {"from": from}, op.#Load & {"from": from},
op.#Export & {source: path}, op.#Export & {source: path},
] ]
} } @dagger(output)
} }
#Glob: { #Glob: {
glob: !="" glob: !="" @dagger(input)
filenames: [...string] filenames: [...string] @dagger(input)
from: dagger.#Artifact from: dagger.#Artifact @dagger(input)
files: (_#glob & {"glob": glob, "from": from}).data files: (_#glob & {"glob": glob, "from": from}).data @dagger(output)
// trim suffix because ls always ends with newline // trim suffix because ls always ends with newline
filenames: strings.Split(strings.TrimSuffix(files, "\n"), "\n") filenames: strings.Split(strings.TrimSuffix(files, "\n"), "\n") @dagger(output)
} }
_#glob: { _#glob: {

View File

@ -7,9 +7,9 @@ import (
// Base Google Cloud Config // Base Google Cloud Config
#Config: { #Config: {
// GCP region // GCP region
region: string region: string @dagger(input)
// GCP projcet // GCP projcet
project: string project: string @dagger(input)
// GCP service key // GCP service key
serviceKey: dagger.#Secret serviceKey: dagger.#Secret @dagger(input)
} }

View File

@ -40,5 +40,5 @@ import (
source: "/token.txt" source: "/token.txt"
}, },
] ]
} } @dagger(output)
} }

View File

@ -11,10 +11,10 @@ import (
config: gcp.#Config config: gcp.#Config
// GKE cluster name // GKE cluster name
clusterName: string clusterName: string @dagger(input)
// Kubectl version // Kubectl version
version: *"v1.19.9" | string version: *"v1.19.9" | string @dagger(input)
// kubeconfig is the generated kube configuration file // kubeconfig is the generated kube configuration file
kubeconfig: { kubeconfig: {
@ -54,7 +54,7 @@ import (
format: "string" format: "string"
}, },
] ]
} } @dagger(output)
} }
#Code: #""" #Code: #"""

View File

@ -7,9 +7,9 @@ import (
// A git repository // A git repository
#Repository: { #Repository: {
remote: string remote: string @dagger(input)
ref: string ref: string @dagger(input)
subdir: string | *"" subdir: string | *"" @dagger(input)
#up: [ #up: [
op.#FetchGit & { op.#FetchGit & {

View File

@ -11,8 +11,8 @@ import (
// A standalone go environment // A standalone go environment
#Container: { #Container: {
// Go version to use // Go version to use
version: *"1.16" | string version: *"1.16" | string @dagger(input)
source: dagger.#Artifact source: dagger.#Artifact @dagger(input)
os.#Container & { os.#Container & {
env: CGO_ENABLED: "0" env: CGO_ENABLED: "0"
@ -38,16 +38,18 @@ import (
#Go: { #Go: {
// Go version to use // Go version to use
version: *"1.16" | string version: *"1.16" | string @dagger(input)
// Arguments to the Go binary // Arguments to the Go binary
args: [...string] args: [...string] @dagger(input)
// Source Directory to build // Source Directory to build
source: dagger.#Artifact source: dagger.#Artifact @dagger(input)
// Environment variables // Environment variables
env: [string]: string env: {
[string]: string @dagger(input)
}
#up: [ #up: [
op.#FetchContainer & { op.#FetchContainer & {
@ -70,30 +72,32 @@ import (
#Build: { #Build: {
// Go version to use // Go version to use
version: *#Go.version | string version: *#Go.version | string @dagger(input)
// Source Directory to build // Source Directory to build
source: dagger.#Artifact source: dagger.#Artifact @dagger(input)
// Packages to build // Packages to build
packages: *"." | string packages: *"." | string @dagger(input)
// Target architecture // Target architecture
arch: *"amd64" | string arch: *"amd64" | string @dagger(input)
// Target OS // Target OS
os: *"linux" | string os: *"linux" | string @dagger(input)
// Build tags to use for building // Build tags to use for building
tags: *"" | string tags: *"" | string @dagger(input)
// LDFLAGS to use for linking // LDFLAGS to use for linking
ldflags: *"" | string ldflags: *"" | string @dagger(input)
// Specify the targeted binary name // Specify the targeted binary name
output: string output: string @dagger(output)
env: [string]: string env: {
[string]: string @dagger(input)
}
#up: [ #up: [
op.#Copy & { op.#Copy & {
@ -111,13 +115,13 @@ import (
#Test: { #Test: {
// Go version to use // Go version to use
version: *#Go.version | string version: *#Go.version | string @dagger(input)
// Source Directory to build // Source Directory to build
source: dagger.#Artifact source: dagger.#Artifact @dagger(input)
// Packages to test // Packages to test
packages: *"." | string packages: *"." | string @dagger(input)
#Go & { #Go & {
"version": version "version": version

View File

@ -13,26 +13,28 @@ import (
// A Yarn package. // A Yarn package.
#Package: { #Package: {
// Application source code // Application source code
source: dagger.#Artifact source: dagger.#Artifact @dagger(input)
// Environment variables // Environment variables
env: [string]: string env: {
[string]: string @dagger(input)
}
// Write the contents of `environment` to this file, // Write the contents of `environment` to this file,
// in the "envfile" format. // in the "envfile" format.
writeEnvFile: string | *"" writeEnvFile: string | *"" @dagger(input)
// Read build output from this directory // Read build output from this directory
// (path must be relative to working directory). // (path must be relative to working directory).
buildDir: string | *"build" buildDir: string | *"build" @dagger(input)
// Run this yarn script // Run this yarn script
script: string | *"build" script: string | *"build" @dagger(input)
build: os.#Dir & { build: os.#Dir & {
from: ctr from: ctr
path: "/build" path: "/build"
} } @dagger(output)
ctr: os.#Container & { ctr: os.#Container & {
image: alpine.#Image & { image: alpine.#Image & {

View File

@ -11,47 +11,47 @@ import (
// Install a Helm chart // Install a Helm chart
#Chart: { #Chart: {
// Helm deployment name // Helm deployment name
name: string name: string @dagger(input)
// Helm chart to install from source // Helm chart to install from source
chartSource: dagger.#Artifact chartSource: dagger.#Artifact @dagger(input)
// Helm chart to install from repository // Helm chart to install from repository
chart?: string chart?: string @dagger(input)
// Helm chart repository (defaults to stable) // Helm chart repository (defaults to stable)
repository: *"https://charts.helm.sh/stable" | string repository: *"https://charts.helm.sh/stable" | string @dagger(input)
// Helm values (either a YAML string or a Cue structure) // Helm values (either a YAML string or a Cue structure)
values?: string values?: string @dagger(input)
// Kubernetes Namespace to deploy to // Kubernetes Namespace to deploy to
namespace: string namespace: string @dagger(input)
// Helm action to apply // Helm action to apply
action: *"installOrUpgrade" | "install" | "upgrade" action: *"installOrUpgrade" | "install" | "upgrade" @dagger(input)
// time to wait for any individual Kubernetes operation (like Jobs for hooks) // time to wait for any individual Kubernetes operation (like Jobs for hooks)
timeout: string | *"5m" timeout: string | *"5m" @dagger(input)
// if set, will wait until all Pods, PVCs, Services, and minimum number of // if set, will wait until all Pods, PVCs, Services, and minimum number of
// Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state // Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state
// before marking the release as successful. // before marking the release as successful.
// It will wait for as long as timeout // It will wait for as long as timeout
wait: *true | bool wait: *true | bool @dagger(input)
// if set, installation process purges chart on fail. // if set, installation process purges chart on fail.
// The wait option will be set automatically if atomic is used // The wait option will be set automatically if atomic is used
atomic: *true | bool atomic: *true | bool @dagger(input)
// Kube config file // Kube config file
kubeconfig: dagger.#Secret kubeconfig: dagger.#Secret @dagger(input)
// Helm version // Helm version
version: *"3.5.2" | string version: *"3.5.2" | string @dagger(input)
// Kubectl version // Kubectl version
kubectlVersion: *"v1.19.9" | string kubectlVersion: *"v1.19.9" | string @dagger(input)
#up: [ #up: [
op.#Load & { op.#Load & {

View File

@ -47,19 +47,19 @@ import (
#Apply: { #Apply: {
// Kubernetes config to deploy // Kubernetes config to deploy
source: dagger.#Artifact source: dagger.#Artifact @dagger(input)
// Kubernetes config to deploy inlined in a string // Kubernetes config to deploy inlined in a string
sourceInline?: string sourceInline?: string @dagger(input)
// Kubernetes Namespace to deploy to // Kubernetes Namespace to deploy to
namespace: string namespace: string @dagger(input)
// Version of kubectl client // Version of kubectl client
version: *"v1.19.9" | string version: *"v1.19.9" | string @dagger(input)
// Kube config file // Kube config file
kubeconfig: dagger.#Secret kubeconfig: dagger.#Secret @dagger(input)
#code: #""" #code: #"""
kubectl create namespace "$KUBE_NAMESPACE" || true kubectl create namespace "$KUBE_NAMESPACE" || true

View File

@ -8,7 +8,7 @@ import (
#Kustomization: { #Kustomization: {
// Kustomize binary version // Kustomize binary version
version: *"v3.8.7" | string version: *"v3.8.7" | string @dagger(input)
#code: #""" #code: #"""
[ -e /usr/local/bin/kubectl ] || { [ -e /usr/local/bin/kubectl ] || {
@ -46,13 +46,13 @@ import (
// Apply a Kubernetes Kustomize folder // Apply a Kubernetes Kustomize folder
#Kustomize: { #Kustomize: {
// Kubernetes source // Kubernetes source
source: dagger.#Artifact source: dagger.#Artifact @dagger(input)
// Optional Kustomization file // Optional Kustomization file
kustomization: string kustomization: string @dagger(input)
// Kustomize binary version // Kustomize binary version
version: *"v3.8.7" | string version: *"v3.8.7" | string @dagger(input)
#code: #""" #code: #"""
cp /kustomization.yaml /source | true cp /kustomization.yaml /source | true

View File

@ -10,10 +10,10 @@ import (
#Account: { #Account: {
// Use this Netlify account name // Use this Netlify account name
// (also referred to as "team" in the Netlify docs) // (also referred to as "team" in the Netlify docs)
name: string | *"" name: string | *"" @dagger(input)
// Netlify authentication token // Netlify authentication token
token: dagger.#Secret token: dagger.#Secret @dagger(input)
} }
// A Netlify site // A Netlify site
@ -22,40 +22,40 @@ import (
account: #Account account: #Account
// Contents of the application to deploy // Contents of the application to deploy
contents: dagger.#Artifact contents: dagger.#Artifact @dagger(input)
// Deploy to this Netlify site // Deploy to this Netlify site
name: string name: string @dagger(input)
// Host the site at this address // Host the site at this address
customDomain?: string customDomain?: string @dagger(input)
// Create the Netlify site if it doesn't exist? // Create the Netlify site if it doesn't exist?
create: bool | *true create: bool | *true @dagger(input)
// Website url // Website url
url: { url: {
os.#File & { os.#File & {
from: ctr from: ctr
path: "/netlify/url" path: "/netlify/url"
} }
}.read.data }.read.data @dagger(output)
// Unique Deploy URL // Unique Deploy URL
deployUrl: { deployUrl: {
os.#File & { os.#File & {
from: ctr from: ctr
path: "/netlify/deployUrl" path: "/netlify/deployUrl"
} }
}.read.data }.read.data @dagger(output)
// Logs URL for this deployment // Logs URL for this deployment
logsUrl: { logsUrl: {
os.#File & { os.#File & {
from: ctr from: ctr
path: "/netlify/logsUrl" path: "/netlify/logsUrl"
} }
}.read.data }.read.data @dagger(output)
ctr: os.#Container & { ctr: os.#Container & {
image: alpine.#Image & { image: alpine.#Image & {

View File

@ -8,13 +8,17 @@ import (
) )
#Configuration: { #Configuration: {
version: string | *"latest" version: string | *"latest" @dagger(input)
source: dagger.#Artifact source: dagger.#Artifact @dagger(input)
tfvars?: [string]: _ tfvars?: {
...
}
env: [string]: string env: {
[string]: string @dagger(input)
}
state: #up: [ state: #up: [
op.#FetchContainer & { op.#FetchContainer & {
@ -61,5 +65,5 @@ import (
}, },
] ]
... ...
} } @dagger(output)
} }

View File

@ -285,3 +285,38 @@ setup() {
"foo": "bar" "foo": "bar"
}' }'
} }
@test "dagger input list" {
"$DAGGER" init
dagger_new_with_plan list "$TESTDIR"/cli/input/list
"$DAGGER" input text cfg.str "foobar" -e "list"
out="$("$DAGGER" input list -e "list")"
outAll="$("$DAGGER" input list --all -e "list")"
#note: this is the recommended way to use pipes with bats
run bash -c "echo \"$out\" | grep awsConfig.accessKey | grep '#Secret' | grep false"
assert_success
run bash -c "echo \"$out\" | grep cfgInline.source | grep '#Artifact' | grep false | grep 'source dir'"
assert_success
run bash -c "echo \"$outAll\" | grep cfg2"
assert_failure
run bash -c "echo \"$out\" | grep cfgInline.strDef | grep string | grep 'yolo (default)' | grep false"
assert_success
run bash -c "echo \"$out\" | grep cfg.num"
assert_failure
run bash -c "echo \"$outAll\" | grep cfg.num | grep int"
assert_success
run bash -c "echo \"$out\" | grep cfg.strSet"
assert_failure
run bash -c "echo \"$outAll\" | grep cfg.strSet | grep string | grep pipo"
assert_success
}

View File

@ -0,0 +1,44 @@
package main
import (
"dagger.io/dagger"
"dagger.io/aws"
)
awsConfig: aws.#Config & {
// force region
region: "us-east-1"
}
#A: {
// source dir
source: dagger.#Artifact @dagger(input)
sourceNotInput: dagger.#Artifact
// a secret
key: dagger.#Secret @dagger(input)
keyNotInput: dagger.#Secret
// a string
str: string @dagger(input)
strSet: "pipo" @dagger(input)
strDef: *"yolo" | string @dagger(input)
// a number
num: int | *42 @dagger(input)
numNotInput: int
// aws config
cfg: awsConfig
}
cfgInline: {
#A
}
cfg: #A & {
// force this key
num: 21
}
cfg2: cfg

View File

@ -1,50 +0,0 @@
package main
foo: string
name: string | *"world"
message: "Hello, \(name)!"
optional?: string
missing: [string]: string
bar: {
a: string
#c: string
b: int @dagger(computed)
}
// may be missing
#inputs: {
hello: string
missing: *"" | string
}
// substitute
let A = string
let B = bar.a
//let Ba = bar.a
//let Bb = bar.b
let D = "hello"
refd: {
a: string
b: {
ref1: a
ref2: A
aa: B
bb: D
}
#c: C: string
}
#fld1: string
exec: {
cmd: string
#up: [{foo: string}]
}
list: [...string]