diff --git a/cmd/extension/extension_zip.go b/cmd/extension/extension_zip.go index 9b487d49..f3ea986b 100644 --- a/cmd/extension/extension_zip.go +++ b/cmd/extension/extension_zip.go @@ -6,11 +6,13 @@ import ( "os/exec" "path" "path/filepath" + "time" cp "github.com/otiai10/copy" "github.com/spf13/cobra" "github.com/FriendsOfShopware/shopware-cli/extension" + "github.com/FriendsOfShopware/shopware-cli/internal/telemetry" "github.com/FriendsOfShopware/shopware-cli/logging" ) @@ -24,6 +26,8 @@ var extensionZipCmd = &cobra.Command{ Short: "Zip a Extension", Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + duration := time.Now() + extPath, err := filepath.Abs(args[0]) if err != nil { return err @@ -187,7 +191,15 @@ var extensionZipCmd = &cobra.Command{ return fmt.Errorf("create zip file: %w", err) } - logging.FromContext(cmd.Context()).Infof("Created file %s", fileName) + seconds := time.Since(duration).Seconds() + + telemetry.Track("extension_zip", map[string]interface{}{ + "duration": seconds, + "changelog-generated": extCfg.Changelog.Enabled, + "ai-changelog": extCfg.Changelog.AiEnabled, + }) + + logging.FromContext(cmd.Context()).Infof("Created file %s in %f seconds", fileName, seconds) return nil }, diff --git a/go.mod b/go.mod index 726f4b80..aa779813 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/posthog/posthog-go v0.0.0-20240315130956-036dfa9f3555 // indirect github.com/tidwall/gjson v1.17.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect diff --git a/go.sum b/go.sum index 83af6658..492a6aef 100644 --- a/go.sum +++ b/go.sum @@ -81,6 +81,8 @@ github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posthog/posthog-go v0.0.0-20240315130956-036dfa9f3555 h1:RqJZxk2VAaZYCCk4ZVo7iLqp4a03LWitjE0tNIMyvMU= +github.com/posthog/posthog-go v0.0.0-20240315130956-036dfa9f3555/go.mod h1:QjlpryJtfYLrZF2GUkAhejH4E7WlDbdKkvOi5hLmkdg= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= diff --git a/internal/system/cache.go b/internal/system/cache.go index 03d6b68f..da8e5cc6 100644 --- a/internal/system/cache.go +++ b/internal/system/cache.go @@ -10,3 +10,9 @@ func GetShopwareCliCacheDir() string { return path.Join(cacheDir, "shopware-cli") } + +func GetShopwareCliConfigDir() string { + configDir, _ := os.UserConfigDir() + + return path.Join(configDir, "shopware-cli") +} diff --git a/internal/telemetry/id.go b/internal/telemetry/id.go new file mode 100644 index 00000000..d920fe35 --- /dev/null +++ b/internal/telemetry/id.go @@ -0,0 +1,62 @@ +package telemetry + +import ( + "crypto/sha256" + "encoding/hex" + "os" + "path" + + "github.com/FriendsOfShopware/shopware-cli/internal/system" + "github.com/google/uuid" +) + +// gatherDistinctId returns a distinct ID for the current user, so we anoynmously track the usage of the CLI +func gatherDistinctId() string { + if os.Getenv("CI") == "true" { + return gatherByCI() + } + + return gatherByMachine() +} + +func gatherByCI() string { + if os.Getenv("GITHUB_REPOSITORY") != "" { + return hash(os.Getenv("GITHUB_REPOSITORY")) + } + + // GitLab + if os.Getenv("CI_PROJECT_NAME") != "" { + return hash(os.Getenv("CI_PROJECT_NAME")) + } + + // Bitbucket + if os.Getenv("BITBUCKET_REPO_FULL_NAME") != "" { + return hash(os.Getenv("BITBUCKET_REPO_FULL_NAME")) + } + + // We cannot determine the CI system, so we generate a random UUID + return hash(uuid.New().String()) +} + +func gatherByMachine() string { + configDir := system.GetShopwareCliConfigDir() + + telemetryFile := path.Join(configDir, "telemetry-id") + + if contents, err := os.ReadFile(telemetryFile); err == nil { + return string(contents) + } + + if _, err := os.Stat(configDir); os.IsNotExist(err) { + _ = os.MkdirAll(configDir, 0o700) + } + + id := hash(uuid.New().String()) + _ = os.WriteFile(telemetryFile, []byte(id), 0o600) + + return id +} + +func hash(s string) string { + return hex.EncodeToString(sha256.New().Sum([]byte(s))) +} diff --git a/internal/telemetry/posthog.go b/internal/telemetry/posthog.go new file mode 100644 index 00000000..ed4554b5 --- /dev/null +++ b/internal/telemetry/posthog.go @@ -0,0 +1,53 @@ +package telemetry + +import ( + "os" + + "github.com/posthog/posthog-go" +) + +var ( + client posthog.Client + distinctId = "" +) + +func Init() { + doNotTrack := os.Getenv("DO_NOT_TRACK") + + if doNotTrack == "1" { + return + } + + var err error + client, err = posthog.NewWithConfig( + "phc_bkTsf3bZw70kXFoqMj3Qkeo3dtzC3x1JvuhamdJI8mJ", + posthog.Config{ + Endpoint: "https://eu.posthog.com", + }, + ) + if err != nil { + return + } + + distinctId = gatherDistinctId() +} + +func Track(event string, properties map[string]interface{}) { + if client == nil { + return + } + + client.Enqueue(posthog.Capture{ + Event: event, + DistinctId: distinctId, + Properties: properties, + }) +} + +func Close() { + if client == nil { + return + } + + client.Close() +} diff --git a/main.go b/main.go index 1f143fe0..4c07b2cd 100644 --- a/main.go +++ b/main.go @@ -4,10 +4,14 @@ import ( "context" "github.com/FriendsOfShopware/shopware-cli/cmd" + "github.com/FriendsOfShopware/shopware-cli/internal/telemetry" "github.com/FriendsOfShopware/shopware-cli/logging" ) func main() { + telemetry.Init() + defer telemetry.Close() + logger := logging.NewLogger() cmd.Execute(logging.WithLogger(context.Background(), logger)) }