Skip to content

Commit d3e9c10

Browse files
committed
Add output values to plan
Signed-off-by: Helder Correia <[email protected]>
1 parent 0309c07 commit d3e9c10

File tree

7 files changed

+154
-11
lines changed

7 files changed

+154
-11
lines changed

cmd/dagger/cmd/up.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -158,16 +158,11 @@ func europaUp(ctx context.Context, cl *client.Client, args ...string) error {
158158
return err
159159
}
160160

161-
if output := viper.GetString("output"); output != "" {
162-
data := computed.JSON().PrettyString()
163-
if output == "-" {
164-
fmt.Println(data)
165-
return nil
166-
}
167-
err := os.WriteFile(output, []byte(data), 0600)
168-
if err != nil {
169-
lg.Fatal().Err(err).Str("path", output).Msg("failed to write output")
170-
}
161+
format := viper.GetString("output-format")
162+
file := viper.GetString("output")
163+
164+
if err := plan.ListOutputs(ctx, p, computed, format, file); err != nil {
165+
lg.Fatal().Err(err).Msg("failed to write output values")
171166
}
172167

173168
return nil
@@ -224,7 +219,8 @@ func checkInputs(ctx context.Context, env *environment.Environment) error {
224219

225220
func init() {
226221
upCmd.Flags().BoolP("force", "f", false, "Force up, disable inputs check")
227-
upCmd.Flags().String("output", "", "Write computed output. Prints on stdout if set to-")
222+
upCmd.Flags().String("output", "", "Write computed output values. Prints on stdout if empty")
223+
upCmd.Flags().String("output-format", "plain", "Format for output values (plain, json, yaml)")
228224
upCmd.Flags().StringArrayP("with", "w", []string{}, "")
229225
upCmd.Flags().StringP("target", "t", "", "Run a single target of the DAG (for debugging only)")
230226
upCmd.Flags().Bool("no-vendor", false, "Force up, disable inputs check")

pkg/dagger.io/dagger/plan.cue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package dagger
1818
directories: [name=string]: _#outputDirectory
1919
// Export a string to a file
2020
files: [name=string]: _#outputFile
21+
// Write values to stdout or a file
22+
values?: [name=string]: _
2123
}
2224

2325
// Forward network services to and from the client

plan/outputvalues.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package plan
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"fmt"
7+
"os"
8+
"text/tabwriter"
9+
10+
"cuelang.org/go/cue"
11+
"cuelang.org/go/pkg/encoding/yaml"
12+
"github.com/rs/zerolog/log"
13+
"go.dagger.io/dagger/cmd/dagger/cmd/common"
14+
"go.dagger.io/dagger/compiler"
15+
)
16+
17+
func ListOutputs(ctx context.Context, p *Plan, computed *compiler.Value, format, file string) error {
18+
lg := log.Ctx(ctx)
19+
path := cue.ParsePath("outputs.values")
20+
21+
if !p.Source().LookupPath(path).Exists() {
22+
return nil
23+
}
24+
25+
out := compiler.NewValue()
26+
27+
if err := out.FillPath(cue.MakePath(), p.Source()); err != nil {
28+
return err
29+
}
30+
31+
if err := out.FillPath(cue.MakePath(), computed); err != nil {
32+
return err
33+
}
34+
35+
vals := out.LookupPath(path)
36+
37+
// Avoid confusion on missing values by forcing concreteness
38+
if err := vals.IsConcreteR(); err != nil {
39+
return err
40+
}
41+
42+
s, err := decodeOutput(vals, format)
43+
if err != nil {
44+
return err
45+
}
46+
47+
if file == "" {
48+
lg.Info().Msg(fmt.Sprintf("Output:\n%v", s))
49+
return nil
50+
}
51+
52+
return os.WriteFile(file, []byte(s), 0600)
53+
}
54+
55+
func decodeOutput(vals *compiler.Value, format string) (string, error) {
56+
switch format {
57+
case "json":
58+
s := vals.JSON().PrettyString()
59+
return s, nil
60+
61+
case "yaml":
62+
s, err := yaml.Marshal(vals.Cue())
63+
if err != nil {
64+
return "", err
65+
}
66+
return s, nil
67+
68+
// The simplest and default case is to have outputs as
69+
// a struct of strings and print it as a table.
70+
case "plain", "":
71+
buf := new(bytes.Buffer)
72+
w := tabwriter.NewWriter(buf, 0, 4, 2, ' ', 0)
73+
fmt.Fprintln(w, "Field\tValue")
74+
75+
if fields, err := vals.Fields(); err == nil {
76+
for _, out := range fields {
77+
fmt.Fprintf(w, "%s\t%s\n", out.Label(), common.FormatValue(out.Value))
78+
}
79+
}
80+
81+
w.Flush()
82+
return buf.String(), nil
83+
}
84+
85+
return "", fmt.Errorf("invalid --output-format %q", format)
86+
}

tests/plan.bats

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,30 @@ setup() {
153153
assert [ ! -f "./test" ]
154154
}
155155

156+
@test "plan/outputs/values" {
157+
cd "$TESTDIR"/plan/outputs/values
158+
rm -rf "./test.json"
159+
160+
run "$DAGGER" --europa up ./not_exists.cue
161+
refute_output --partial "Output:"
162+
163+
run "$DAGGER" --europa up ./non_concrete.cue
164+
assert_failure
165+
166+
run "$DAGGER" --europa up ./computed.cue
167+
assert_output --partial "Output:"
168+
assert_output --partial "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3"
169+
170+
run "$DAGGER" --europa up --output-format yaml ./computed.cue
171+
assert_output --partial "digest: sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3"
172+
173+
"$DAGGER" --europa up --output-format json --output test.json ./computed.cue
174+
run jq -re '.test.config.cmd[0]' "./test.json"
175+
assert_output "/bin/sh"
176+
177+
rm -rf "./test.json"
178+
}
179+
156180
@test "plan/platform" {
157181
cd "$TESTDIR"
158182

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package main
2+
3+
import "dagger.io/dagger"
4+
5+
dagger.#Plan & {
6+
actions: image: dagger.#Pull & {
7+
source: "alpine:3.15.0@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3"
8+
}
9+
outputs: values: {
10+
test: {
11+
config: actions.image.config
12+
foo: "bar"
13+
}
14+
digest: actions.image.digest
15+
}
16+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package main
2+
3+
import "dagger.io/dagger"
4+
5+
dagger.#Plan & {
6+
outputs: values: test: {
7+
ok: "foobar"
8+
notok: string
9+
}
10+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package main
2+
3+
import "dagger.io/dagger"
4+
5+
dagger.#Plan & {
6+
actions: image: dagger.#Pull & {
7+
source: "alpine:3.15.0@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3"
8+
}
9+
}

0 commit comments

Comments
 (0)