Skip to content

Commit a175e15

Browse files
committed
Better observe flow
1 parent 8829663 commit a175e15

File tree

7 files changed

+120
-114
lines changed

7 files changed

+120
-114
lines changed

cmd/gather.go

Lines changed: 50 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,35 @@
22
package cmd
33

44
import (
5-
"errors"
65
"fmt"
76
"os"
87
"time"
98

109
"github.com/spf13/cobra"
1110

1211
"github.com/kalverra/octometrics/gather"
12+
"github.com/kalverra/octometrics/observe"
1313
)
1414

1515
var (
16-
githubToken string
17-
forceUpdate bool
16+
githubToken string
17+
githubClient *gather.GitHubClient
18+
forceUpdate bool
19+
noObserve bool
1820
)
1921

2022
var gatherCmd = &cobra.Command{
2123
Use: "gather",
2224
Short: "Gather metrics from GitHub",
2325
PreRunE: func(_ *cobra.Command, _ []string) error {
24-
if owner == "" {
25-
return errors.New("owner must be provided")
26-
}
27-
if repo == "" {
28-
return errors.New("repo must be provided")
29-
}
30-
31-
setCount := 0
32-
if commitSHA != "" {
33-
setCount++
34-
}
35-
if workflowRunID != 0 {
36-
setCount++
37-
}
38-
if pullRequestNumber != 0 {
39-
setCount++
40-
}
41-
if setCount > 1 {
42-
return errors.New("only one of commit SHA, workflow run ID or pull request number can be provided")
26+
if err := cfg.ValidateGather(); err != nil {
27+
return err
4328
}
44-
if setCount == 0 {
45-
return errors.New("one of commit SHA, workflow run ID or pull request number must be provided")
29+
var err error
30+
githubClient, err = gather.NewGitHubClient(logger, githubToken, nil)
31+
if err != nil {
32+
return fmt.Errorf("failed to create GitHub client: %w", err)
4633
}
47-
4834
return nil
4935
},
5036
RunE: func(_ *cobra.Command, _ []string) error {
@@ -54,15 +40,16 @@ var gatherCmd = &cobra.Command{
5440

5541
startTime := time.Now()
5642

57-
logger = logger.With().Str("owner", owner).Str("repo", repo).Logger()
43+
logger = logger.With().Str("owner", cfg.Owner).Str("repo", cfg.Repo).Logger()
5844

59-
if workflowRunID != 0 {
60-
logger = logger.With().Int64("workflow_run_id", workflowRunID).Logger()
61-
} else if pullRequestNumber != 0 {
62-
logger = logger.With().Int("pull_request_number", pullRequestNumber).Logger()
45+
if cfg.WorkflowRunID != 0 {
46+
logger = logger.With().Int64("workflow_run_id", cfg.WorkflowRunID).Logger()
47+
} else if cfg.PullRequestNumber != 0 {
48+
logger = logger.With().Int("pull_request_number", cfg.PullRequestNumber).Logger()
6349
}
6450

6551
logger.Info().Msg("Gathering data")
52+
fmt.Println("Gathering data...")
6653

6754
opts := []gather.Option{}
6855

@@ -71,40 +58,50 @@ var gatherCmd = &cobra.Command{
7158
}
7259

7360
var err error
74-
if workflowRunID != 0 {
75-
_, _, err = gather.WorkflowRun(logger, githubClient, owner, repo, workflowRunID, opts...)
76-
} else if pullRequestNumber != 0 {
77-
_, err = gather.PullRequest(logger, githubClient, owner, repo, pullRequestNumber, opts...)
78-
} else if commitSHA != "" {
79-
_, err = gather.Commit(logger, githubClient, owner, repo, commitSHA, opts...)
61+
if cfg.WorkflowRunID != 0 {
62+
_, _, err = gather.WorkflowRun(logger, githubClient, cfg.Owner, cfg.Repo, cfg.WorkflowRunID, opts...)
63+
} else if cfg.PullRequestNumber != 0 {
64+
_, err = gather.PullRequest(logger, githubClient, cfg.Owner, cfg.Repo, cfg.PullRequestNumber, opts...)
65+
} else if cfg.CommitSHA != "" {
66+
_, err = gather.Commit(logger, githubClient, cfg.Owner, cfg.Repo, cfg.CommitSHA, opts...)
8067
}
8168
if err != nil {
8269
return err
8370
}
8471

8572
logger.Info().Str("duration", time.Since(startTime).String()).Msg("Gathered data")
86-
return nil
73+
fmt.Println("Gathered data")
74+
75+
if noObserve {
76+
return nil
77+
}
78+
79+
var pagePath string
80+
if cfg.WorkflowRunID != 0 {
81+
pagePath = fmt.Sprintf("/%s/%s/workflow_runs/%d.html", cfg.Owner, cfg.Repo, cfg.WorkflowRunID)
82+
} else if cfg.PullRequestNumber != 0 {
83+
pagePath = fmt.Sprintf("/%s/%s/pull_requests/%d.html", cfg.Owner, cfg.Repo, cfg.PullRequestNumber)
84+
} else if cfg.CommitSHA != "" {
85+
pagePath = fmt.Sprintf("/%s/%s/commits/%s.html", cfg.Owner, cfg.Repo, cfg.CommitSHA)
86+
}
87+
88+
if err := os.RemoveAll(observe.OutputDir); err != nil {
89+
return fmt.Errorf("failed to clean observe output: %w", err)
90+
}
91+
return observe.Interactive(logger, githubClient, pagePath)
8792
},
8893
}
8994

9095
func init() {
91-
gatherCmd.Flags().BoolVarP(&forceUpdate, "force-update", "u", false, "Force update of existing data")
92-
gatherCmd.Flags().StringVarP(&owner, "owner", "o", "", "Repository owner")
93-
gatherCmd.Flags().StringVarP(&repo, "repo", "r", "", "Repository name")
94-
gatherCmd.Flags().StringVarP(&commitSHA, "commit-sha", "c", "", "Commit SHA")
95-
gatherCmd.Flags().Int64VarP(&workflowRunID, "workflow-run-id", "w", 0, "Workflow run ID")
96-
gatherCmd.Flags().IntVarP(&pullRequestNumber, "pull-request-number", "p", 0, "Pull request number")
9796
gatherCmd.Flags().
98-
StringVarP(&githubToken, "github-token", "t", "", fmt.Sprintf("GitHub API token (can also be set via %s)", gather.GitHubTokenEnvVar))
99-
100-
if err := gatherCmd.MarkFlagRequired("owner"); err != nil {
101-
fmt.Printf("ERROR: Failed to mark owner flag as required: %s\n", err.Error())
102-
os.Exit(1)
103-
}
104-
if err := gatherCmd.MarkFlagRequired("repo"); err != nil {
105-
fmt.Printf("ERROR: Failed to mark repo flag as required: %s\n", err.Error())
106-
os.Exit(1)
107-
}
97+
BoolVar(&noObserve, "no-observe", false, "Skip launching the interactive observer after gathering")
98+
gatherCmd.Flags().BoolVarP(&forceUpdate, "force-update", "u", false, "Force update of existing data")
99+
gatherCmd.Flags().StringP("owner", "o", "", "Repository owner")
100+
gatherCmd.Flags().StringP("repo", "r", "", "Repository name")
101+
gatherCmd.Flags().StringP("commit-sha", "c", "", "Commit SHA")
102+
gatherCmd.Flags().Int64P("workflow_run_id", "w", 0, "Workflow run ID")
103+
gatherCmd.Flags().IntP("pull_request_number", "p", 0, "Pull request number")
104+
gatherCmd.Flags().StringP("github_token", "t", "", "GitHub API token (env: GITHUB_TOKEN)")
108105

109106
rootCmd.AddCommand(gatherCmd)
110107
}

cmd/observe.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
11
package cmd
22

33
import (
4+
"fmt"
45
"os"
56

67
"github.com/spf13/cobra"
78

9+
"github.com/kalverra/octometrics/gather"
810
"github.com/kalverra/octometrics/observe"
911
)
1012

11-
var (
12-
outputTypes []string
13-
)
14-
1513
var observeCmd = &cobra.Command{
1614
Use: "observe",
1715
Short: "Observe metrics from GitHub",
1816
PreRunE: func(_ *cobra.Command, _ []string) error {
17+
var err error
18+
githubClient, err = gather.NewGitHubClient(logger, githubToken, nil)
19+
if err != nil {
20+
return fmt.Errorf("failed to create GitHub client: %w", err)
21+
}
1922
return os.RemoveAll(observe.OutputDir)
2023
},
2124
RunE: func(_ *cobra.Command, _ []string) error {
22-
logger.Debug().
23-
Strs("output-types", outputTypes).
24-
Msg("observe flags")
25-
2625
// if workflowRunID != 0 {
2726
// err := observe.WorkflowRun(githubClient, owner, repo, workflowRunID, outputTypes)
2827
// if err != nil {
@@ -37,12 +36,10 @@ var observeCmd = &cobra.Command{
3736
// }
3837
// }
3938

40-
return observe.Interactive(logger, githubClient)
39+
return observe.Interactive(logger, githubClient, "")
4140
},
4241
}
4342

4443
func init() {
4544
rootCmd.AddCommand(observeCmd)
46-
47-
observeCmd.Flags().StringArrayVar(&outputTypes, "output-types", []string{"html", "md"}, "Output types to generate")
4845
}

cmd/octometrics.go

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/rs/zerolog"
1212
"github.com/spf13/cobra"
1313

14-
"github.com/kalverra/octometrics/gather"
1514
"github.com/kalverra/octometrics/internal/config"
1615
"github.com/kalverra/octometrics/logging"
1716
)
@@ -20,10 +19,9 @@ const (
2019
logFileName = "octometrics.log.json"
2120
)
2221

23-
// Persistent base command flags
2422
var (
25-
githubClient *gather.GitHubClient
26-
logger zerolog.Logger
23+
cfg *config.Config
24+
logger zerolog.Logger
2725
)
2826

2927
// These variables are set at build time and describe the version and build of the application
@@ -46,15 +44,6 @@ func versionInfo() string {
4644
)
4745
}
4846

49-
// Flag values shared between other commands
50-
var (
51-
owner string
52-
repo string
53-
commitSHA string
54-
workflowRunID int64
55-
pullRequestNumber int
56-
)
57-
5847
var rootCmd = &cobra.Command{
5948
Use: "octometrics",
6049
Short: "See metrics and profiling of your GitHub Actions",
@@ -65,31 +54,20 @@ Octometrics aims to help you easily visualize what your workflows look like, hel
6554
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
6655
var err error
6756

68-
cfg, err := config.Load(config.WithFlags(cmd.Flags()))
57+
cfg, err = config.Load(config.WithFlags(cmd.Flags()))
6958
if err != nil {
7059
return fmt.Errorf("failed to load config: %w", err)
7160
}
7261
logger, err = logging.New(logging.WithFileName(logFileName), logging.WithLevel(cfg.LogLevel))
7362
if err != nil {
7463
return fmt.Errorf("failed to setup logging: %w", err)
7564
}
76-
77-
githubClient, err = gather.NewGitHubClient(logger, githubToken, nil)
78-
if err != nil {
79-
return fmt.Errorf("failed to create GitHub client: %w", err)
80-
}
8165
return nil
8266
},
83-
Run: func(cmd *cobra.Command, _ []string) {
84-
err := cmd.Help()
85-
if err != nil {
86-
logger.Fatal().Err(err).Msg("Failed to print help message")
87-
}
88-
},
8967
}
9068

9169
func init() {
92-
rootCmd.PersistentFlags().String("log-level", config.DefaultLogLevel, "Log level")
70+
rootCmd.PersistentFlags().String("log-level", config.DefaultLogLevel, "Level for detailed logging")
9371
}
9472

9573
// Execute runs the root command for octometrics.

gather/gather.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"errors"
66
"fmt"
77
"net/http"
8-
"os"
98
"strconv"
109
"strings"
1110
"time"
@@ -19,8 +18,6 @@ import (
1918

2019
// GitHub API constants for authentication, timeouts, and data storage.
2120
const (
22-
//nolint:gosec // Env var for getting token
23-
GitHubTokenEnvVar = "GITHUB_TOKEN"
2421
//nolint:gosec // This is a mock token for testing purposes
2522
MockGitHubToken = "mock_github_token"
2623
timeoutDur = 10 * time.Second
@@ -100,16 +97,6 @@ func NewGitHubClient(
10097
githubToken string,
10198
optionalNext http.RoundTripper,
10299
) (*GitHubClient, error) {
103-
switch {
104-
case githubToken != "":
105-
logger.Debug().Msg("Using GitHub token from flag")
106-
case os.Getenv(GitHubTokenEnvVar) != "":
107-
githubToken = os.Getenv(GitHubTokenEnvVar)
108-
logger.Debug().Msg("Using GitHub token from environment variable")
109-
default:
110-
logger.Warn().Msg("GitHub token not provided, will likely hit rate limits quickly")
111-
}
112-
113100
var (
114101
err error
115102
next http.RoundTripper

internal/config/config.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,22 @@
22
package config
33

44
import (
5+
"errors"
6+
"fmt"
7+
58
"github.com/spf13/pflag"
69
"github.com/spf13/viper"
710
)
811

912
// Config is the configuration for the application.
1013
type Config struct {
11-
LogLevel string `mapstructure:"log_level"`
14+
LogLevel string `mapstructure:"log_level"`
15+
GitHubToken string `mapstructure:"github_token"`
16+
Owner string `mapstructure:"owner"`
17+
Repo string `mapstructure:"repo"`
18+
CommitSHA string `mapstructure:"commit_sha"`
19+
WorkflowRunID int64 `mapstructure:"workflow_run_id"`
20+
PullRequestNumber int `mapstructure:"pull_request_number"`
1221
}
1322

1423
const (
@@ -43,6 +52,7 @@ func Load(opts ...LoadOption) (*Config, error) {
4352
v.AddConfigPath(".")
4453

4554
v.SetDefault("log_level", DefaultLogLevel)
55+
v.AutomaticEnv()
4656

4757
for _, opt := range opts {
4858
if err := opt(v); err != nil {
@@ -51,7 +61,9 @@ func Load(opts ...LoadOption) (*Config, error) {
5161
}
5262

5363
if err := v.ReadInConfig(); err != nil {
54-
return nil, err
64+
if _, ok := err.(viper.ConfigFileNotFoundError); !ok { // If the config file is not found, we don't need to return an error
65+
return nil, fmt.Errorf("failed to read config file: %w", err)
66+
}
5567
}
5668

5769
cfg := &Config{}
@@ -61,3 +73,36 @@ func Load(opts ...LoadOption) (*Config, error) {
6173

6274
return cfg, nil
6375
}
76+
77+
// ValidateGather validates the configuration for the gather command.
78+
func (c *Config) ValidateGather() error {
79+
if c.Owner == "" {
80+
return errors.New("owner is required")
81+
}
82+
if c.Repo == "" {
83+
return errors.New("repo is required")
84+
}
85+
86+
setCount := 0
87+
if c.CommitSHA != "" {
88+
setCount++
89+
}
90+
if c.WorkflowRunID != 0 {
91+
setCount++
92+
}
93+
if c.PullRequestNumber != 0 {
94+
setCount++
95+
}
96+
if setCount > 1 {
97+
return errors.New("only one of commit SHA, workflow run ID or pull request number can be provided")
98+
}
99+
if setCount == 0 {
100+
return errors.New("one of commit SHA, workflow run ID or pull request number must be provided")
101+
}
102+
103+
if c.GitHubToken == "" {
104+
fmt.Println("WARNING:GitHub token not provided, will likely hit rate limits quickly")
105+
}
106+
107+
return nil
108+
}

0 commit comments

Comments
 (0)