diff --git a/src/engine/segment.go b/src/engine/segment.go index 6ceebd0b..a7b514d5 100644 --- a/src/engine/segment.go +++ b/src/engine/segment.go @@ -3,14 +3,15 @@ package engine import ( "errors" "fmt" + "runtime/debug" + "strings" + "time" + "oh-my-posh/environment" "oh-my-posh/properties" "oh-my-posh/segments" "oh-my-posh/shell" "oh-my-posh/template" - "runtime/debug" - "strings" - "time" ) // Segment represent a single segment and it's configuration @@ -108,6 +109,8 @@ const ( FLUTTER SegmentType = "flutter" // FOSSIL writes the fossil status FOSSIL SegmentType = "fossil" + // GCP writes the active GCP context + GCP SegmentType = "gcp" // GIT represents the git status and information GIT SegmentType = "git" // GOLANG writes which go version is currently active @@ -281,6 +284,7 @@ func (segment *Segment) mapSegmentWithWriter(env environment.Environment) error EXIT: &segments.Exit{}, FLUTTER: &segments.Flutter{}, FOSSIL: &segments.Fossil{}, + GCP: &segments.Gcp{}, GIT: &segments.Git{}, GOLANG: &segments.Golang{}, HASKELL: &segments.Haskell{}, diff --git a/src/segments/gcp.go b/src/segments/gcp.go new file mode 100644 index 00000000..4081f34f --- /dev/null +++ b/src/segments/gcp.go @@ -0,0 +1,76 @@ +package segments + +import ( + "errors" + "oh-my-posh/environment" + "oh-my-posh/properties" + "path" + "path/filepath" + + "gopkg.in/ini.v1" +) + +type Gcp struct { + props properties.Properties + env environment.Environment + + Account string + Project string + Region string + Error string +} + +func (g *Gcp) Template() string { + return " {{ if .Error }}{{ .Error }}{{ else }}{{ .Project }}{{ end }} " +} + +func (g *Gcp) Init(props properties.Properties, env environment.Environment) { + g.props = props + g.env = env +} + +func (g *Gcp) Enabled() bool { + cfgDir := g.getConfigDirectory() + configFile, err := g.getActiveConfig(cfgDir) + if err != nil { + g.Error = err.Error() + return true + } + + cfgpath := path.Join(cfgDir, "configurations", "config_"+configFile) + + cfg, err := ini.Load(cfgpath) + if err != nil { + g.Error = "GCLOUD CONFIG ERROR" + return true + } + + g.Project = cfg.Section("core").Key("project").String() + g.Account = cfg.Section("core").Key("account").String() + g.Region = cfg.Section("compute").Key("region").String() + + return true +} + +func (g *Gcp) getActiveConfig(cfgDir string) (string, error) { + ap := path.Join(cfgDir, "active_config") + absolutePath, err := filepath.Abs(ap) + if err != nil { + return "", err + } + + fileContent := g.env.FileContent(absolutePath) + if len(fileContent) == 0 { + return "", errors.New("NO ACTIVE CONFIG FOUND") + } + return fileContent, nil +} + +func (g *Gcp) getConfigDirectory() string { + cfgDir := g.env.Getenv("CLOUDSDK_CONFIG") + if len(cfgDir) == 0 { + cfgDir = path.Join(g.env.Home(), ".config", "gcloud") + } + + return cfgDir +} diff --git a/src/segments/gcp_test.go b/src/segments/gcp_test.go new file mode 100644 index 00000000..46d33b56 --- /dev/null +++ b/src/segments/gcp_test.go @@ -0,0 +1,62 @@ +package segments + +import ( + "path" + "path/filepath" + "testing" + + "oh-my-posh/mock" + + "github.com/stretchr/testify/assert" +) + +func TestGcpSegment(t *testing.T) { + standardTemplate := "{{ if .Error }}{{ .Error }}{{ else }}{{ .Project }}{{ end }}" + allTemplate := "{{.Project}} :: {{.Region}} :: {{.Account}}" + + cases := []struct { + Case string + Template string + ConfigPath string + ActiveConfig string + ExpectedEnabled bool + ExpectedString string + }{ + { + Case: "all information", + Template: allTemplate, + ConfigPath: "../test/", + ActiveConfig: "gcptest", + ExpectedEnabled: true, + ExpectedString: "test-test-test :: europe-test1 :: test@example.com", + }, + { + Case: "non-existent config file", + Template: standardTemplate, + ConfigPath: "../invalid/", + ActiveConfig: "nofile", + ExpectedEnabled: true, + ExpectedString: "GCLOUD CONFIG ERROR", + }, + { + Case: "invalid active config file", + Template: standardTemplate, + ConfigPath: "../invalid/", + ActiveConfig: "", + ExpectedEnabled: true, + ExpectedString: "NO ACTIVE CONFIG FOUND", + }, + } + + for _, tc := range cases { + env := new(mock.MockedEnvironment) + env.On("Getenv", "CLOUDSDK_CONFIG").Return(tc.ConfigPath) + fcPath, _ := filepath.Abs(path.Join(tc.ConfigPath, "active_config")) + env.On("FileContent", fcPath).Return(tc.ActiveConfig) + g := &Gcp{ + env: env, + } + assert.Equal(t, tc.ExpectedEnabled, g.Enabled(), tc.Case) + assert.Equal(t, tc.ExpectedString, renderTemplate(env, tc.Template, g), tc.Case) + } +} diff --git a/src/test/configurations/config_gcptest b/src/test/configurations/config_gcptest new file mode 100644 index 00000000..e54e6df9 --- /dev/null +++ b/src/test/configurations/config_gcptest @@ -0,0 +1,6 @@ +[core] +account = test@example.com +project = test-test-test + +[compute] +region = europe-test1 diff --git a/themes/schema.json b/themes/schema.json index 76e15734..3b69077a 100644 --- a/themes/schema.json +++ b/themes/schema.json @@ -236,6 +236,7 @@ "executiontime", "flutter", "fossil", + "gcp", "git", "go", "haskell", @@ -2726,6 +2727,19 @@ } } } + }, + { + "if": { + "properties": { + "type": { + "const": "gcp" + } + } + }, + "then": { + "title": "GCP Segment", + "description": "https://ohmyposh.dev/docs/segments/gcp" + } } ] } diff --git a/website/docs/segments/gcp.mdx b/website/docs/segments/gcp.mdx new file mode 100644 index 00000000..6be8deb5 --- /dev/null +++ b/website/docs/segments/gcp.mdx @@ -0,0 +1,41 @@ +--- +id: gcp +title: GCP Context +sidebar_label: GCP +--- + +## What + +Display the currently active GCP project, region and account + +## Sample Configuration + +```json +{ + "type": "gcp", + "style": "powerline", + "powerline_symbol": "\uE0B0", + "foreground": "#ffffff", + "background": "#47888d", + "template": " \uE7B2 {{.Project} :: {{.Account}} " +} +``` + +## Template ([info][templates]) + +:::note default template + +``` template +{{ if .Error }}{{ .Error }}{{ else }}{{ .Project }}{{ end }} +``` + +::: + +### Properties + +- `.Project`: `string` - the currently active project +- `.Account`: `string` - the currently active account +- `.Region`: `string` - default region for the active context +- `.Error`: `string` - contains any error messages generated when trying to load the GCP config + +[templates]: /docs/configuration/templates diff --git a/website/sidebars.js b/website/sidebars.js index 1e5397a7..ae9f0183 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -69,6 +69,7 @@ module.exports = { "segments/exit", "segments/flutter", "segments/fossil", + "segments/gcp", "segments/git", "segments/poshgit", "segments/golang",