Skip to content

Commit b8fb237

Browse files
authored
Merge pull request #3733 from tejal29/add_survey_command
Add a new survey command to show Skaffold User Survey form url.
2 parents 43d66c4 + 6dfea44 commit b8fb237

File tree

19 files changed

+420
-26
lines changed

19 files changed

+420
-26
lines changed

cmd/skaffold/app/cmd/cmd.go

+1
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ func NewSkaffoldCommand(out, err io.Writer) *cobra.Command {
158158
rootCmd.AddCommand(NewCmdSchema())
159159

160160
rootCmd.AddCommand(NewCmdGeneratePipeline())
161+
rootCmd.AddCommand(NewCmdSurvey())
161162

162163
templates.ActsAsRootCommand(rootCmd, nil, groups...)
163164
rootCmd.PersistentFlags().StringVarP(&v, "verbosity", "v", constants.DefaultLogLevel.String(), "Log level (debug, info, warn, error, fatal, panic)")

cmd/skaffold/app/cmd/config/set.go

+1-19
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,10 @@ import (
2020
"context"
2121
"fmt"
2222
"io"
23-
"io/ioutil"
2423
"reflect"
2524
"strconv"
2625
"strings"
2726

28-
yaml "gopkg.in/yaml.v2"
29-
3027
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
3128
)
3229

@@ -159,22 +156,7 @@ func writeConfig(cfg *config.ContextConfig) error {
159156
fullConfig.ContextConfigs = append(fullConfig.ContextConfigs, cfg)
160157
}
161158
}
162-
return writeFullConfig(fullConfig)
163-
}
164-
165-
func writeFullConfig(cfg *config.GlobalConfig) error {
166-
contents, err := yaml.Marshal(cfg)
167-
if err != nil {
168-
return fmt.Errorf("marshaling config: %w", err)
169-
}
170-
configFileOrDefault, err := config.ResolveConfigFile(configFile)
171-
if err != nil {
172-
return err
173-
}
174-
if err := ioutil.WriteFile(configFileOrDefault, contents, 0644); err != nil {
175-
return fmt.Errorf("writing config file: %w", err)
176-
}
177-
return nil
159+
return config.WriteFullConfig(configFile, fullConfig)
178160
}
179161

180162
func logSetConfigForUser(out io.Writer, key string, value string) {

cmd/skaffold/app/cmd/survey.go

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
Copyright 2020 The Skaffold Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cmd
18+
19+
import (
20+
"context"
21+
"io"
22+
23+
"github.com/spf13/cobra"
24+
25+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/survey"
26+
)
27+
28+
func NewCmdSurvey() *cobra.Command {
29+
return NewCmd("survey").
30+
WithDescription("Show Skaffold survey url").
31+
NoArgs(showSurvey)
32+
}
33+
34+
func showSurvey(context context.Context, out io.Writer) error {
35+
s := survey.New(opts.GlobalConfig)
36+
return s.OpenSurveyForm(context, out)
37+
}

docs/content/en/docs/references/cli/_index.md

+16
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ Other Commands:
8787
credits Export third party notices to given path (./skaffold-credits by default)
8888
diagnose Run a diagnostic on Skaffold
8989
schema List and print json schemas used to validate skaffold.yaml configuration
90+
survey Show Skaffold survey url
9091
version Print the version information
9192
9293
Use "skaffold <command> --help" for more information about a given command.
@@ -843,6 +844,21 @@ Usage:
843844
Use "skaffold options" for a list of global command-line options (applies to all commands).
844845
845846
847+
```
848+
849+
### skaffold survey
850+
851+
Show Skaffold survey url
852+
853+
```
854+
855+
856+
Usage:
857+
skaffold survey [options]
858+
859+
Use "skaffold options" for a list of global command-line options (applies to all commands).
860+
861+
846862
```
847863

848864
### skaffold version

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ require (
4646
github.com/opencontainers/go-digest v1.0.0-rc1.0.20190228220655-ac19fd6e7483
4747
github.com/opencontainers/image-spec v1.0.1
4848
github.com/openzipkin/zipkin-go v0.2.2 // indirect
49+
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
4950
github.com/rakyll/statik v0.1.7
5051
github.com/rjeczalik/notify v0.9.2
5152
github.com/russross/blackfriday/v2 v2.0.1

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,8 @@ github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtb
617617
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
618618
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
619619
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
620+
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98=
621+
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
620622
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
621623
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
622624
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=

pkg/skaffold/config/util.go

+46
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ var (
5555
ReadConfigFile = readConfigFileCached
5656
GetConfigForCurrentKubectx = getConfigForCurrentKubectx
5757
current = time.Now
58+
59+
// update global config with the time the survey was last taken
60+
updateLastTaken = "skaffold config set --survey --global last-taken %s"
5861
)
5962

6063
// readConfigFileCached reads the specified file and returns the contents
@@ -259,3 +262,46 @@ func lessThan(date string, duration time.Duration) bool {
259262
}
260263
return current().Sub(t) < duration
261264
}
265+
266+
func UpdateGlobalSurveyTaken(configFile string) error {
267+
// Today's date
268+
today := current().Format(time.RFC3339)
269+
ai := fmt.Sprintf(updateLastTaken, today)
270+
aiErr := fmt.Errorf("could not automatically update the survey timestamp - please run `%s`", ai)
271+
272+
configFile, err := ResolveConfigFile(configFile)
273+
if err != nil {
274+
return aiErr
275+
}
276+
fullConfig, err := ReadConfigFile(configFile)
277+
if err != nil {
278+
return aiErr
279+
}
280+
if fullConfig.Global == nil {
281+
fullConfig.Global = &ContextConfig{}
282+
}
283+
if fullConfig.Global.Survey == nil {
284+
fullConfig.Global.Survey = &SurveyConfig{}
285+
}
286+
fullConfig.Global.Survey.LastTaken = today
287+
err = WriteFullConfig(configFile, fullConfig)
288+
if err != nil {
289+
return aiErr
290+
}
291+
return err
292+
}
293+
294+
func WriteFullConfig(configFile string, cfg *GlobalConfig) error {
295+
contents, err := yaml.Marshal(cfg)
296+
if err != nil {
297+
return fmt.Errorf("marshaling config: %w", err)
298+
}
299+
configFileOrDefault, err := ResolveConfigFile(configFile)
300+
if err != nil {
301+
return err
302+
}
303+
if err := ioutil.WriteFile(configFileOrDefault, contents, 0644); err != nil {
304+
return fmt.Errorf("writing config file: %w", err)
305+
}
306+
return nil
307+
}

pkg/skaffold/config/util_test.go

+60
Original file line numberDiff line numberDiff line change
@@ -499,3 +499,63 @@ func TestGetDefaultRepo(t *testing.T) {
499499
})
500500
}
501501
}
502+
503+
func TestUpdateGlobalSurveyTaken(t *testing.T) {
504+
tests := []struct {
505+
description string
506+
cfg string
507+
expectedCfg *GlobalConfig
508+
}{
509+
{
510+
description: "update global context when context is empty",
511+
expectedCfg: &GlobalConfig{
512+
Global: &ContextConfig{Survey: &SurveyConfig{}},
513+
ContextConfigs: []*ContextConfig{},
514+
},
515+
},
516+
{
517+
description: "update global context when survey config is not nil",
518+
cfg: `
519+
global:
520+
survey:
521+
last-prompted: "some date"
522+
kubeContexts: []`,
523+
expectedCfg: &GlobalConfig{
524+
Global: &ContextConfig{Survey: &SurveyConfig{LastPrompted: "some date"}},
525+
ContextConfigs: []*ContextConfig{},
526+
},
527+
},
528+
{
529+
description: "update global context when survey config last taken is in past",
530+
cfg: `
531+
global:
532+
survey:
533+
last-taken: "some date in past"
534+
kubeContexts: []`,
535+
expectedCfg: &GlobalConfig{
536+
Global: &ContextConfig{Survey: &SurveyConfig{}},
537+
ContextConfigs: []*ContextConfig{},
538+
},
539+
},
540+
}
541+
for _, test := range tests {
542+
testutil.Run(t, test.description, func(t *testutil.T) {
543+
cfg := t.TempFile("config", []byte(test.cfg))
544+
testTime := time.Now()
545+
t.Override(&ReadConfigFile, ReadConfigFileNoCache)
546+
t.Override(&current, func() time.Time {
547+
return testTime
548+
})
549+
550+
// update the time
551+
err := UpdateGlobalSurveyTaken(cfg)
552+
t.CheckNoError(err)
553+
554+
actualConfig, cfgErr := ReadConfigFile(cfg)
555+
t.CheckNoError(cfgErr)
556+
// update time in expected cfg.
557+
test.expectedCfg.Global.Survey.LastTaken = testTime.Format(time.RFC3339)
558+
t.CheckDeepEqual(test.expectedCfg, actualConfig)
559+
})
560+
}
561+
}

pkg/skaffold/survey/survey.go

+44-6
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,64 @@ limitations under the License.
1717
package survey
1818

1919
import (
20+
"context"
21+
"fmt"
2022
"io"
2123
"os"
2224

23-
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
25+
"github.com/pkg/browser"
26+
"github.com/sirupsen/logrus"
27+
28+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
2429
)
2530

2631
const (
27-
Prompt = `Help improve Skaffold! Take a 10 seconds anonymous survey by running
28-
$skaffold survey`
32+
Prompt = `Help improve Skaffold! Take a 10-second anonymous survey by running
33+
skaffold survey`
34+
35+
URL = "https://forms.gle/BMTbGQXLWSdn7vEs6"
2936
)
3037

31-
// for testing
3238
var (
39+
Form = fmt.Sprintf(`Thank you for offering your feedback on Skaffold! Understanding your experiences and opinions helps us make Skaffold better for you and other users.
40+
Our survey can be found here: %s
41+
42+
To permanently disable the survey prompt, run:
43+
skaffold config set --survey --global disable-prompt true`, URL)
44+
45+
// for testing
3346
isStdOut = stdOut
47+
open = browser.OpenURL
3448
)
3549

36-
func DisplaySurveyForm(out io.Writer) {
50+
type Runner struct {
51+
configFile string
52+
}
53+
54+
func New(configFile string) *Runner {
55+
return &Runner{
56+
configFile: configFile,
57+
}
58+
}
59+
60+
func DisplaySurveyPrompt(out io.Writer) {
3761
if isStdOut(out) {
38-
color.Default.Fprintln(out, Prompt)
62+
fmt.Fprintln(out, Prompt)
63+
}
64+
}
65+
66+
func (s *Runner) OpenSurveyForm(_ context.Context, out io.Writer) error {
67+
_, err := fmt.Fprintln(out, Form)
68+
if err != nil {
69+
return err
70+
}
71+
if err := open(URL); err != nil {
72+
logrus.Debugf("could not open url %s", URL)
73+
return err
3974
}
75+
// Currently we will only update the global survey taken
76+
// When prompting for the survey, we need to use the same field.
77+
return config.UpdateGlobalSurveyTaken(s.configFile)
4078
}
4179

4280
func stdOut(out io.Writer) bool {

pkg/skaffold/survey/survey_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ func TestDisplaySurveyForm(t *testing.T) {
4444
testutil.Run(t, test.description, func(t *testutil.T) {
4545
mock := func(io.Writer) bool { return test.mockStdOut }
4646
t.Override(&isStdOut, mock)
47+
mockOpen := func(string) error { return nil }
48+
t.Override(&open, mockOpen)
4749
var buf bytes.Buffer
48-
DisplaySurveyForm(&buf)
50+
DisplaySurveyPrompt(&buf)
4951
t.CheckDeepEqual(test.expected, buf.String())
5052
})
5153
}

vendor/github.com/pkg/browser/LICENSE

+23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)