Skip to content

Commit

Permalink
ci: add ldflags for binary version & analytics (#37)
Browse files Browse the repository at this point in the history
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
2 people authored and zulkhair committed Feb 15, 2024
1 parent ce45009 commit 57c10a3
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 119 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ jobs:
output_name=world_${{ matrix.goos }}_${{ matrix.goarch }}
[ ${{ matrix.goos }} = "windows" ] && output_name+=".exe"
env GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o $output_name ./cmd/world
## Get version from tag name
bin_version=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
## Handle if event coming from PR, not release/tag
if [ "${{ github.event_name }}" == "pull_request" ]; then bin_version=${{ github.event.pull_request.head.sha }}; fi
env GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -ldflags "-X main.AppVersion=$bin_version -X main.SentryDsn=${{ secrets.SENTRY_DSN }} -X main.PosthogApiKey=${{ secrets.POSTHOG_API_KEY }}" -o $output_name ./cmd/world
echo "output_name=$output_name" >> $GITHUB_OUTPUT
- name: Compress Build Binary
uses: a7ul/[email protected]
Expand Down
68 changes: 12 additions & 56 deletions cmd/world/main.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package main

import (
"github.com/denisbrodbeck/machineid"
ph "github.com/posthog/posthog-go"
"log"
"os"
"pkg.world.dev/world-cli/common/logger"
"pkg.world.dev/world-cli/posthog"
"time"

"github.com/getsentry/sentry-go"
"github.com/rs/zerolog/log"

"pkg.world.dev/world-cli/cmd/world/root"
_ "pkg.world.dev/world-cli/common/logger"
"pkg.world.dev/world-cli/telemetry"
)

// This variable will be overridden by ldflags during build
Expand All @@ -21,11 +18,6 @@ var (
SentryDsn string
)

const (
postInstallationEvent = "World CLI Installation"
runningEvent = "World CLI Running"
)

func init() {
// Set default app version in case not provided by ldflags
if AppVersion == "" {
Expand All @@ -36,60 +28,24 @@ func init() {

func main() {
// Sentry initialization
if SentryDsn != "" {
err := sentry.Init(sentry.ClientOptions{
Dsn: SentryDsn,
EnableTracing: true,
TracesSampleRate: 1.0,
ProfilesSampleRate: 1.0,
AttachStacktrace: true,
})
if err != nil {
log.Fatalf("sentry.Init: %s", err)
}
// Handle panic
defer func() {
err := recover()
if err != nil {
sentry.CurrentHub().Recover(err)
}
telemetry.SentryInit(SentryDsn)
defer telemetry.SentryFlush()

// Flush buffered events before the program terminates.
// Set the timeout to the maximum duration the program can afford to wait.
sentry.Flush(time.Second * 5)
}()
}
// Set logger sentry hook
log.Logger = log.Logger.Hook(telemetry.SentryHook{})

// Posthog Initialization
posthog.Init(PosthogApiKey)
defer posthog.Close()

// Obtain the machine ID
machineID, err := machineid.ProtectedID("world-cli")
if err != nil {
logger.Error(err)
}

// Create capture event for posthog
event := ph.Capture{
DistinctId: machineID,
Timestamp: time.Now(),
Properties: map[string]interface{}{
"version": AppVersion,
"command": os.Args,
},
}
telemetry.PosthogInit(PosthogApiKey)
defer telemetry.PosthogClose()

// Capture event post installation
if len(os.Args) > 1 && os.Args[1] == "post-installation" {
event.Event = postInstallationEvent
posthog.CaptureEvent(event)
telemetry.PosthogCaptureEvent(AppVersion, telemetry.PostInstallationEvent)
return
}

// Capture event running
event.Event = runningEvent
posthog.CaptureEvent(event)
telemetry.PosthogCaptureEvent(AppVersion, telemetry.RunningEvent)

root.Execute()
}
3 changes: 0 additions & 3 deletions common/logger/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ func init() {
lgr = lgr.With().Caller().Logger()
}

// Set hook for sentry capture error message
lgr = lgr.Hook(SentryHook{})

log.Logger = lgr
}

Expand Down
21 changes: 0 additions & 21 deletions common/logger/sentry.go

This file was deleted.

38 changes: 0 additions & 38 deletions posthog/posthog.go

This file was deleted.

132 changes: 132 additions & 0 deletions telemetry/posthog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package telemetry

import (
"os"
"path/filepath"
"time"

"github.com/denisbrodbeck/machineid"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
)

var (
posthogClient posthog.Client
posthogInitialized bool
lastLoggedTime time.Time
)

const (
PostInstallationEvent = "World CLI Installation"
RunningEvent = "World CLI Running"
timestampFile = ".worldcli"
)

// Init Posthog initialization
func PosthogInit(posthogApiKey string) {
if posthogApiKey != "" {
posthogClient = posthog.New(posthogApiKey)
posthogInitialized = true

// get last logged time
lastTime, err := getLastLoggedTime()
if err != nil {
log.Err(err).Msg("Cannot get last logged time")
}

lastLoggedTime = lastTime

// Update last visited timestamp
err = updateLastLoggedTime(time.Now())
if err != nil {
log.Err(err).Msg("Cannot update last logged time")
}
}
}

// getLastLoggedTime reads the last visited timestamp from the file.
func getLastLoggedTime() (time.Time, error) {
filePath, err := getTimestampFilePath()
if err != nil {
return time.Time{}, err
}

if _, err = os.Stat(filePath); os.IsNotExist(err) {
// Return a zero time if the file does not exist
return time.Time{}, nil
}

data, err := os.ReadFile(filePath)
if err != nil {
return time.Time{}, err
}

timestamp, err := time.Parse(time.DateOnly, string(data))
if err != nil {
return time.Time{}, err
}

return timestamp, nil
}

// getTimestampFilePath returns the path to the timestamp file.
func getTimestampFilePath() (string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(homeDir, timestampFile), nil
}

// updateLastLoggedTime updates the last visited timestamp in the file.
func updateLastLoggedTime(timestamp time.Time) error {
filePath, err := getTimestampFilePath()
if err != nil {
return err
}

data := []byte(timestamp.Format(time.DateOnly))

return os.WriteFile(filePath, data, 0644)
}

// isSameDay checks if two timestamps are from the same day.
func isSameDay(time1, time2 time.Time) bool {
return time1.Year() == time2.Year() &&
time1.Month() == time2.Month() &&
time1.Day() == time2.Day()
}

func PosthogCaptureEvent(context, event string) {
if posthogInitialized && (!isSameDay(lastLoggedTime, time.Now()) || event != RunningEvent) {
// Obtain the machine ID
machineID, err := machineid.ProtectedID("world-cli")
if err != nil {
log.Err(err).Msg("Cannot get machine id")
return
}

// Capture the event
err = posthogClient.Enqueue(posthog.Capture{
DistinctId: machineID,
Timestamp: time.Now(),
Event: event,
Properties: map[string]interface{}{
"context": context,
},
})
if err != nil {
log.Err(err).Msg("Cannot capture event")
}
}
}

func PosthogClose() {
if posthogInitialized {
err := posthogClient.Close()
if err != nil {
log.Err(err).Msg("Cannot close posthog client")
}
posthogInitialized = false
}
}
62 changes: 62 additions & 0 deletions telemetry/sentry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package telemetry

import (
"fmt"
"github.com/getsentry/sentry-go"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"time"
)

var (
sentryInitialized bool
)

// SentryInit initialize sentry
func SentryInit(sentryDsn string) {
if sentryDsn != "" {
err := sentry.Init(sentry.ClientOptions{
Dsn: sentryDsn,
EnableTracing: true,
TracesSampleRate: 1.0,
ProfilesSampleRate: 1.0,
AttachStacktrace: true,
})
if err != nil {
log.Err(err).Msg("Cannot initialize sentry")
return
}

sentryInitialized = true
}
}

func SentryFlush() {
if sentryInitialized {
err := recover()
if err != nil {
sentry.CurrentHub().Recover(err)
}

// Flush buffered events before the program terminates.
// Set the timeout to the maximum duration the program can afford to wait.
sentry.Flush(time.Second * 5)
sentryInitialized = false
}
}

// SentryHook is a custom hook that implements zerolog.Hook interface
type SentryHook struct{}

// Run is called for every log event and implements the zerolog.Hook interface
func (h SentryHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
if sentryInitialized {
// Capture error message
sentry.CaptureException(fmt.Errorf(msg))
}
}

// Levels returns the log levels that this hook should be triggered for
func (h SentryHook) Levels() []zerolog.Level {
return []zerolog.Level{zerolog.ErrorLevel, zerolog.FatalLevel}
}

0 comments on commit 57c10a3

Please sign in to comment.