diff --git a/cmd/build.go b/cmd/build.go index c32ed969c..04ae56d43 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -1,8 +1,10 @@ package cmd import ( + "github.com/CircleCI-Public/circleci-cli/cmd/create_telemetry" "github.com/CircleCI-Public/circleci-cli/local" "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/CircleCI-Public/circleci-cli/telemetry" "github.com/spf13/cobra" ) @@ -16,7 +18,13 @@ func newLocalExecuteCommand(config *settings.Config) *cobra.Command { return nil }, RunE: func(cmd *cobra.Command, _ []string) error { - return local.Execute(cmd.Flags(), config, args) + telemetryClient := create_telemetry.CreateTelemetry(config) + defer telemetryClient.Close() + + err := local.Execute(cmd.Flags(), config, args) + _ = telemetryClient.Track(telemetry.CreateLocalExecuteEvent(create_telemetry.GetCommandInformation(cmd, true))) + + return err }, Args: cobra.MinimumNArgs(1), } diff --git a/cmd/namespace.go b/cmd/namespace.go index 0e5fb8cc5..e9c0bf912 100644 --- a/cmd/namespace.go +++ b/cmd/namespace.go @@ -6,8 +6,10 @@ import ( "github.com/CircleCI-Public/circleci-cli/api" "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/cmd/create_telemetry" "github.com/CircleCI-Public/circleci-cli/prompt" "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/CircleCI-Public/circleci-cli/telemetry" "github.com/google/uuid" "github.com/spf13/cobra" ) @@ -68,13 +70,19 @@ Please note that at this time all namespaces created in the registry are world-r return validateToken(opts.cfg) }, RunE: func(cmd *cobra.Command, _ []string) error { + telemetryClient := create_telemetry.CreateTelemetry(config) + defer telemetryClient.Close() + if opts.integrationTesting { opts.tty = createNamespaceTestUI{ confirm: true, } } - return createNamespace(cmd, opts) + err := createNamespace(cmd, opts) + _ = telemetryClient.Track(telemetry.CreateNamespaceEvent(create_telemetry.GetCommandInformation(cmd, true))) + + return err }, Args: cobra.RangeArgs(1, 3), Annotations: make(map[string]string), diff --git a/cmd/orb.go b/cmd/orb.go index a7037c7d8..5134209b0 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -20,11 +20,13 @@ import ( "github.com/CircleCI-Public/circleci-cli/api" "github.com/CircleCI-Public/circleci-cli/api/collaborators" "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/cmd/create_telemetry" "github.com/CircleCI-Public/circleci-cli/filetree" "github.com/CircleCI-Public/circleci-cli/process" "github.com/CircleCI-Public/circleci-cli/prompt" "github.com/CircleCI-Public/circleci-cli/references" "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/CircleCI-Public/circleci-cli/telemetry" "github.com/CircleCI-Public/circleci-cli/version" "github.com/fatih/color" "github.com/pkg/errors" @@ -388,6 +390,10 @@ Please note that at this time all orbs created in the registry are world-readabl opts.args = args opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) + telemetryClient := create_telemetry.CreateTelemetry(config) + defer telemetryClient.Close() + telemetryClient.Track(telemetry.CreateOrbEvent(create_telemetry.GetCommandInformation(cmd, true))) + // PersistentPreRunE overwrites the inherited persistent hook from rootCmd // So we explicitly call it here to retain that behavior. // As of writing this comment, that is only for daily update checks. diff --git a/cmd/orb_test.go b/cmd/orb_test.go index 7b4e917ba..3f773b0c9 100644 --- a/cmd/orb_test.go +++ b/cmd/orb_test.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "os" "os/exec" "path/filepath" "strconv" @@ -16,6 +17,7 @@ import ( "github.com/CircleCI-Public/circleci-cli/api" "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/clitest" + "github.com/CircleCI-Public/circleci-cli/telemetry" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" @@ -23,6 +25,62 @@ import ( "github.com/onsi/gomega/gexec" ) +var _ = Describe("Orb telemetry", func() { + var ( + command *exec.Cmd + orb *clitest.TmpFile + tempSettings *clitest.TempSettings + telemetryDestFilePath string + ) + + BeforeEach(func() { + tempSettings = clitest.WithTempSettings() + telemetryDestFilePath = filepath.Join(tempSettings.Home, "telemetry-content") + orb = clitest.OpenTmpFile(tempSettings.Home, "orb.yml") + command = exec.Command(pathCLI, + "orb", "validate", orb.Path, + "--skip-update-check", + "--token", "token", + "--host", tempSettings.TestServer.URL(), + "--mock-telemetry", telemetryDestFilePath, + ) + }) + + AfterEach(func() { + orb.Close() + tempSettings.Close() + if _, err := os.Stat(telemetryDestFilePath); err == nil || !os.IsNotExist(err) { + os.Remove(telemetryDestFilePath) + } + }) + + It("works", func() { + orb.Write([]byte(`{}`)) + + mockOrbIntrospection(true, "", tempSettings) + + tempSettings.TestServer.AppendHandlers(func(res http.ResponseWriter, req *http.Request) { + res.WriteHeader(http.StatusOK) + res.Write([]byte(`{"orbConfig": {"sourceYaml": "{}", "valid": true, "errors": []} }`)) + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session).Should(gexec.Exit(0)) + + clitest.CompareTelemetryEvent(telemetryDestFilePath, []telemetry.Event{ + telemetry.CreateOrbEvent(telemetry.CommandInfo{ + Name: "validate", + LocalArgs: map[string]string{ + "org-slug": "", + "help": "false", + "org-id": "", + }, + }), + }) + }) +}) + var _ = Describe("Orb integration tests", func() { Describe("Orb help text", func() { It("shows a link to the docs", func() { diff --git a/telemetry/events.go b/telemetry/events.go index c6a20dbff..52620823c 100644 --- a/telemetry/events.go +++ b/telemetry/events.go @@ -12,12 +12,17 @@ type CommandInfo struct { LocalArgs map[string]string } -func localArgsToProperties(cmdInfo CommandInfo) map[string]interface{} { +func createEventFromCommandInfo(name string, cmdInfo CommandInfo) Event { properties := map[string]interface{}{} for key, value := range cmdInfo.LocalArgs { properties[fmt.Sprintf("cmd.flag.%s", key)] = value } - return properties + + return Event{ + Object: fmt.Sprintf("cli-%s", name), + Action: cmdInfo.Name, + Properties: properties, + } } func errorToProperties(err error) map[string]interface{} { @@ -48,11 +53,7 @@ func CreateVersionEvent(version string) Event { } func CreateUpdateEvent(cmdInfo CommandInfo) Event { - return Event{ - Object: "cli-update", - Action: cmdInfo.Name, - Properties: localArgsToProperties(cmdInfo), - } + return createEventFromCommandInfo("update", cmdInfo) } func CreateDiagnosticEvent(err error) Event { @@ -72,17 +73,21 @@ func CreateOpenEvent(err error) Event { } func CreateCompletionCommand(cmdInfo CommandInfo) Event { - return Event{ - Object: "cli-completion", - Action: cmdInfo.Name, - Properties: localArgsToProperties(cmdInfo), - } + return createEventFromCommandInfo("completion", cmdInfo) } func CreateConfigEvent(cmdInfo CommandInfo) Event { - return Event{ - Object: "cli-config", - Action: cmdInfo.Name, - Properties: localArgsToProperties(cmdInfo), - } + return createEventFromCommandInfo("config", cmdInfo) +} + +func CreateLocalExecuteEvent(cmdInfo CommandInfo) Event { + return createEventFromCommandInfo("local", cmdInfo) +} + +func CreateNamespaceEvent(cmdInfo CommandInfo) Event { + return createEventFromCommandInfo("namespace", cmdInfo) +} + +func CreateOrbEvent(cmdInfo CommandInfo) Event { + return createEventFromCommandInfo("orb", cmdInfo) }