detached bcb7dc3

Fix Vertex ADC discovery on Windows

commit
bcb7dc3c9ea3021fabb7d419bf64b4373a637b7b
author
patriceckhart · · 2026-07-14 11:58 UTC
parent
2806fa1
signature
unsigned
6 files · +93 -29
M README.md +12 -7
@@ -554,9 +554,9 @@ Reasoning levels (`--reasoning off|minimum|low|medium|high|xhigh|max`, also conf
554554
555555 You can add additional Gemini model IDs to `models.json` under the `google` provider.
556556
557-### Google Enterprise Agent Platform (formerly Google Vertex AI)
557+### Gemini Enterprise Agent Platform (formerly Google Vertex AI)
558558
559-zot also has built-in support for [Google Enterprise Agent Platform](https://cloud.google.com/products/gemini-enterprise-agent-platform), Google's enterprise/GCP-hosted Gemini endpoint. Unlike the AI Studio `google` provider above, Google Enterprise Agent Platform supports Google Cloud service-account and Application Default Credentials (ADC) auth in addition to a plain API key — no consumer Google login, but full GCP-style credential support.
559+zot also has built-in support for [Gemini Enterprise Agent Platform](https://cloud.google.com/products/gemini-enterprise-agent-platform), Google's enterprise/GCP-hosted Gemini endpoint. Unlike the AI Studio `google` provider above, the `google-vertex` provider supports a Google Cloud API key plus `service_account` and `authorized_user` credential files. It does not support consumer Google login or the complete ADC credential chain, such as metadata-server and workload-identity credentials.
560560
561561 ```bash
562562 zot --provider google-vertex
@@ -569,11 +569,16 @@ Required configuration (env vars, read at construction time):
569569
570570 Credential lookup order for Vertex:
571571
572-1. `GOOGLE_CLOUD_API_KEY` — simplest option; an API key created in the GCP console, sent as `x-goog-api-key`. Used directly, no token exchange.
573-2. `GOOGLE_APPLICATION_CREDENTIALS` — path to a service-account JSON key. zot signs a JWT with the key's private key and exchanges it at `https://oauth2.googleapis.com/token` for a short-lived (1h) access token, cached in memory and refreshed on demand.
574-3. **Default ADC file** — if neither of the above is set, zot falls back to `~/.config/gcloud/application_default_credentials.json`, i.e. whatever `gcloud auth application-default login` produced. Both credential shapes found there are supported:
575- - `type: "service_account"` — same JWT/token-exchange flow as above.
576- - `type: "authorized_user"` — the user-OAuth shape gcloud writes; zot reads the `client_id`/`client_secret`/`refresh_token` and exchanges the refresh token for an access token, with the same caching/refresh behavior.
572+1. `GOOGLE_CLOUD_API_KEY`: simplest option. An API key created in the GCP console is sent as `x-goog-api-key`, without a token exchange.
573+2. `GOOGLE_APPLICATION_CREDENTIALS`: path to a supported credential JSON file.
574+3. **Default ADC file**: if neither of the above is set, zot checks the file written by `gcloud auth application-default login`. This is `~/.config/gcloud/application_default_credentials.json` on Unix systems and `%APPDATA%\gcloud\application_default_credentials.json` on Windows.
575+
576+Two credential file shapes are supported:
577+
578+- `type: "service_account"`: zot signs a JWT with the private key and exchanges it for a short-lived access token.
579+- `type: "authorized_user"`: zot exchanges the stored client ID, client secret, and refresh token for a short-lived access token.
580+
581+Access tokens are cached in memory and refreshed on demand.
577582
578583 If none of these are available, zot errors with `vertex: no auth — set GOOGLE_CLOUD_API_KEY or GOOGLE_APPLICATION_CREDENTIALS`.
579584
@@ -554,9 +554,9 @@ Reasoning levels (`--reasoning off|minimum|low|medium|high|xhigh|max`, also conf
554 554
555 You can add additional Gemini model IDs to `models.json` under the `google` provider.555 You can add additional Gemini model IDs to `models.json` under the `google` provider.
556 556
557-### Google Enterprise Agent Platform (formerly Google Vertex AI)557+### Gemini Enterprise Agent Platform (formerly Google Vertex AI)
558 558
559-zot also has built-in support for [Google Enterprise Agent Platform](https://cloud.google.com/products/gemini-enterprise-agent-platform), Google's enterprise/GCP-hosted Gemini endpoint. Unlike the AI Studio `google` provider above, Google Enterprise Agent Platform supports Google Cloud service-account and Application Default Credentials (ADC) auth in addition to a plain API key — no consumer Google login, but full GCP-style credential support.559+zot also has built-in support for [Gemini Enterprise Agent Platform](https://cloud.google.com/products/gemini-enterprise-agent-platform), Google's enterprise/GCP-hosted Gemini endpoint. Unlike the AI Studio `google` provider above, the `google-vertex` provider supports a Google Cloud API key plus `service_account` and `authorized_user` credential files. It does not support consumer Google login or the complete ADC credential chain, such as metadata-server and workload-identity credentials.
560 560
561 ```bash561 ```bash
562 zot --provider google-vertex562 zot --provider google-vertex
@@ -569,11 +569,16 @@ Required configuration (env vars, read at construction time):
569 569
570 Credential lookup order for Vertex:570 Credential lookup order for Vertex:
571 571
572-1. `GOOGLE_CLOUD_API_KEY` — simplest option; an API key created in the GCP console, sent as `x-goog-api-key`. Used directly, no token exchange.572+1. `GOOGLE_CLOUD_API_KEY`: simplest option. An API key created in the GCP console is sent as `x-goog-api-key`, without a token exchange.
573-2. `GOOGLE_APPLICATION_CREDENTIALS` — path to a service-account JSON key. zot signs a JWT with the key's private key and exchanges it at `https://oauth2.googleapis.com/token` for a short-lived (1h) access token, cached in memory and refreshed on demand.573+2. `GOOGLE_APPLICATION_CREDENTIALS`: path to a supported credential JSON file.
574-3. **Default ADC file** — if neither of the above is set, zot falls back to `~/.config/gcloud/application_default_credentials.json`, i.e. whatever `gcloud auth application-default login` produced. Both credential shapes found there are supported:574+3. **Default ADC file**: if neither of the above is set, zot checks the file written by `gcloud auth application-default login`. This is `~/.config/gcloud/application_default_credentials.json` on Unix systems and `%APPDATA%\gcloud\application_default_credentials.json` on Windows.
575- - `type: "service_account"` — same JWT/token-exchange flow as above.575+
576- - `type: "authorized_user"` — the user-OAuth shape gcloud writes; zot reads the `client_id`/`client_secret`/`refresh_token` and exchanges the refresh token for an access token, with the same caching/refresh behavior.576+Two credential file shapes are supported:
577+
578+- `type: "service_account"`: zot signs a JWT with the private key and exchanges it for a short-lived access token.
579+- `type: "authorized_user"`: zot exchanges the stored client ID, client secret, and refresh token for a short-lived access token.
580+
581+Access tokens are cached in memory and refreshed on demand.
577 582
578 If none of these are available, zot errors with `vertex: no auth — set GOOGLE_CLOUD_API_KEY or GOOGLE_APPLICATION_CREDENTIALS`.583 If none of these are available, zot errors with `vertex: no auth — set GOOGLE_CLOUD_API_KEY or GOOGLE_APPLICATION_CREDENTIALS`.
579 584
M packages/agent/config.go +2 -3
@@ -381,9 +381,8 @@ func ResolveCredentialFull(provider, explicit string) (cred, method, accountID s
381381 if os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") != "" {
382382 return "<adc>", "apikey", "", nil
383383 }
384- // Check for the default ADC path that gcloud writes to
385- if home, err := os.UserHomeDir(); err == nil {
386- adcPath := filepath.Join(home, ".config", "gcloud", "application_default_credentials.json")
384+ // Check for the platform-specific default ADC path that gcloud writes to.
385+ if adcPath, err := auth.GoogleApplicationDefaultCredentialsPath(); err == nil {
387386 if _, err := os.Stat(adcPath); err == nil {
388387 return "<adc>", "apikey", "", nil
389388 }
@@ -381,9 +381,8 @@ func ResolveCredentialFull(provider, explicit string) (cred, method, accountID s
381 if os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") != "" {381 if os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") != "" {
382 return "<adc>", "apikey", "", nil382 return "<adc>", "apikey", "", nil
383 }383 }
384- // Check for the default ADC path that gcloud writes to384+ // Check for the platform-specific default ADC path that gcloud writes to.
385- if home, err := os.UserHomeDir(); err == nil {385+ if adcPath, err := auth.GoogleApplicationDefaultCredentialsPath(); err == nil {
386- adcPath := filepath.Join(home, ".config", "gcloud", "application_default_credentials.json")
387 if _, err := os.Stat(adcPath); err == nil {386 if _, err := os.Stat(adcPath); err == nil {
388 return "<adc>", "apikey", "", nil387 return "<adc>", "apikey", "", nil
389 }388 }
M packages/agent/config_gcp_test.go +23 -17
@@ -4,14 +4,28 @@ import (
44 "os"
55 "path/filepath"
66 "testing"
7+
8+ providerauth "github.com/patriceckhart/zot/packages/provider/auth"
79 )
810
11+func isolateGoogleVertexADC(t *testing.T) string {
12+ t.Helper()
13+ root := t.TempDir()
14+ t.Setenv("HOME", root)
15+ t.Setenv("APPDATA", root)
16+ path, err := providerauth.GoogleApplicationDefaultCredentialsPath()
17+ if err != nil {
18+ t.Fatalf("GoogleApplicationDefaultCredentialsPath: %v", err)
19+ }
20+ return path
21+}
22+
923 // TestResolveCredentialGoogleVertexAPIKey confirms that GOOGLE_CLOUD_API_KEY
1024 // takes top priority for the google-vertex provider and is returned with
1125 // method "apikey".
1226 func TestResolveCredentialGoogleVertexAPIKey(t *testing.T) {
1327 t.Setenv("ZOT_HOME", t.TempDir())
14- t.Setenv("HOME", t.TempDir())
28+ isolateGoogleVertexADC(t)
1529 t.Setenv("GOOGLE_CLOUD_API_KEY", "gcp-key-123")
1630 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")
1731
@@ -33,7 +47,7 @@ func TestResolveCredentialGoogleVertexAPIKey(t *testing.T) {
3347 // the real NewVertex client's ADC-based auth flow.
3448 func TestResolveCredentialGoogleVertexApplicationCredentialsEnv(t *testing.T) {
3549 t.Setenv("ZOT_HOME", t.TempDir())
36- t.Setenv("HOME", t.TempDir())
50+ isolateGoogleVertexADC(t)
3751 t.Setenv("GOOGLE_CLOUD_API_KEY", "")
3852 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/path/to/service-account.json")
3953
@@ -50,21 +64,17 @@ func TestResolveCredentialGoogleVertexApplicationCredentialsEnv(t *testing.T) {
5064 }
5165
5266 // TestResolveCredentialGoogleVertexDefaultADCFile confirms that, absent any
53-// env vars, the presence of the default gcloud ADC file
54-// (~/.config/gcloud/application_default_credentials.json) is detected and
55-// resolves to the "<adc>" sentinel.
67+// env vars, the presence of the platform-specific default gcloud ADC file is
68+// detected and resolves to the "<adc>" sentinel.
5669 func TestResolveCredentialGoogleVertexDefaultADCFile(t *testing.T) {
57- home := t.TempDir()
5870 t.Setenv("ZOT_HOME", t.TempDir())
59- t.Setenv("HOME", home)
71+ adcPath := isolateGoogleVertexADC(t)
6072 t.Setenv("GOOGLE_CLOUD_API_KEY", "")
6173 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")
6274
63- adcDir := filepath.Join(home, ".config", "gcloud")
64- if err := os.MkdirAll(adcDir, 0o755); err != nil {
75+ if err := os.MkdirAll(filepath.Dir(adcPath), 0o755); err != nil {
6576 t.Fatal(err)
6677 }
67- adcPath := filepath.Join(adcDir, "application_default_credentials.json")
6878 if err := os.WriteFile(adcPath, []byte(`{"type":"authorized_user"}`), 0o600); err != nil {
6979 t.Fatal(err)
7080 }
@@ -85,17 +95,14 @@ func TestResolveCredentialGoogleVertexDefaultADCFile(t *testing.T) {
8595 // wins over GOOGLE_APPLICATION_CREDENTIALS and the default ADC file when
8696 // multiple credential sources are present simultaneously.
8797 func TestResolveCredentialGoogleVertexPrecedence(t *testing.T) {
88- home := t.TempDir()
8998 t.Setenv("ZOT_HOME", t.TempDir())
90- t.Setenv("HOME", home)
99+ adcPath := isolateGoogleVertexADC(t)
91100 t.Setenv("GOOGLE_CLOUD_API_KEY", "explicit-key")
92101 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/path/to/sa.json")
93102
94- adcDir := filepath.Join(home, ".config", "gcloud")
95- if err := os.MkdirAll(adcDir, 0o755); err != nil {
103+ if err := os.MkdirAll(filepath.Dir(adcPath), 0o755); err != nil {
96104 t.Fatal(err)
97105 }
98- adcPath := filepath.Join(adcDir, "application_default_credentials.json")
99106 if err := os.WriteFile(adcPath, []byte(`{"type":"authorized_user"}`), 0o600); err != nil {
100107 t.Fatal(err)
101108 }
@@ -117,9 +124,8 @@ func TestResolveCredentialGoogleVertexPrecedence(t *testing.T) {
117124 // auth.json entry), resolution fails with an error rather than silently
118125 // returning an empty credential.
119126 func TestResolveCredentialGoogleVertexNoCredentials(t *testing.T) {
120- home := t.TempDir()
121127 t.Setenv("ZOT_HOME", t.TempDir())
122- t.Setenv("HOME", home)
128+ isolateGoogleVertexADC(t)
123129 t.Setenv("GOOGLE_CLOUD_API_KEY", "")
124130 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")
125131
@@ -4,14 +4,28 @@ import (
4 "os"4 "os"
5 "path/filepath"5 "path/filepath"
6 "testing"6 "testing"
7+
8+ providerauth "github.com/patriceckhart/zot/packages/provider/auth"
7 )9 )
8 10
11+func isolateGoogleVertexADC(t *testing.T) string {
12+ t.Helper()
13+ root := t.TempDir()
14+ t.Setenv("HOME", root)
15+ t.Setenv("APPDATA", root)
16+ path, err := providerauth.GoogleApplicationDefaultCredentialsPath()
17+ if err != nil {
18+ t.Fatalf("GoogleApplicationDefaultCredentialsPath: %v", err)
19+ }
20+ return path
21+}
22+
9 // TestResolveCredentialGoogleVertexAPIKey confirms that GOOGLE_CLOUD_API_KEY23 // TestResolveCredentialGoogleVertexAPIKey confirms that GOOGLE_CLOUD_API_KEY
10 // takes top priority for the google-vertex provider and is returned with24 // takes top priority for the google-vertex provider and is returned with
11 // method "apikey".25 // method "apikey".
12 func TestResolveCredentialGoogleVertexAPIKey(t *testing.T) {26 func TestResolveCredentialGoogleVertexAPIKey(t *testing.T) {
13 t.Setenv("ZOT_HOME", t.TempDir())27 t.Setenv("ZOT_HOME", t.TempDir())
14- t.Setenv("HOME", t.TempDir())28+ isolateGoogleVertexADC(t)
15 t.Setenv("GOOGLE_CLOUD_API_KEY", "gcp-key-123")29 t.Setenv("GOOGLE_CLOUD_API_KEY", "gcp-key-123")
16 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")30 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")
17 31
@@ -33,7 +47,7 @@ func TestResolveCredentialGoogleVertexAPIKey(t *testing.T) {
33 // the real NewVertex client's ADC-based auth flow.47 // the real NewVertex client's ADC-based auth flow.
34 func TestResolveCredentialGoogleVertexApplicationCredentialsEnv(t *testing.T) {48 func TestResolveCredentialGoogleVertexApplicationCredentialsEnv(t *testing.T) {
35 t.Setenv("ZOT_HOME", t.TempDir())49 t.Setenv("ZOT_HOME", t.TempDir())
36- t.Setenv("HOME", t.TempDir())50+ isolateGoogleVertexADC(t)
37 t.Setenv("GOOGLE_CLOUD_API_KEY", "")51 t.Setenv("GOOGLE_CLOUD_API_KEY", "")
38 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/path/to/service-account.json")52 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/path/to/service-account.json")
39 53
@@ -50,21 +64,17 @@ func TestResolveCredentialGoogleVertexApplicationCredentialsEnv(t *testing.T) {
50 }64 }
51 65
52 // TestResolveCredentialGoogleVertexDefaultADCFile confirms that, absent any66 // TestResolveCredentialGoogleVertexDefaultADCFile confirms that, absent any
53-// env vars, the presence of the default gcloud ADC file67+// env vars, the presence of the platform-specific default gcloud ADC file is
54-// (~/.config/gcloud/application_default_credentials.json) is detected and68+// detected and resolves to the "<adc>" sentinel.
55-// resolves to the "<adc>" sentinel.
56 func TestResolveCredentialGoogleVertexDefaultADCFile(t *testing.T) {69 func TestResolveCredentialGoogleVertexDefaultADCFile(t *testing.T) {
57- home := t.TempDir()
58 t.Setenv("ZOT_HOME", t.TempDir())70 t.Setenv("ZOT_HOME", t.TempDir())
59- t.Setenv("HOME", home)71+ adcPath := isolateGoogleVertexADC(t)
60 t.Setenv("GOOGLE_CLOUD_API_KEY", "")72 t.Setenv("GOOGLE_CLOUD_API_KEY", "")
61 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")73 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")
62 74
63- adcDir := filepath.Join(home, ".config", "gcloud")75+ if err := os.MkdirAll(filepath.Dir(adcPath), 0o755); err != nil {
64- if err := os.MkdirAll(adcDir, 0o755); err != nil {
65 t.Fatal(err)76 t.Fatal(err)
66 }77 }
67- adcPath := filepath.Join(adcDir, "application_default_credentials.json")
68 if err := os.WriteFile(adcPath, []byte(`{"type":"authorized_user"}`), 0o600); err != nil {78 if err := os.WriteFile(adcPath, []byte(`{"type":"authorized_user"}`), 0o600); err != nil {
69 t.Fatal(err)79 t.Fatal(err)
70 }80 }
@@ -85,17 +95,14 @@ func TestResolveCredentialGoogleVertexDefaultADCFile(t *testing.T) {
85 // wins over GOOGLE_APPLICATION_CREDENTIALS and the default ADC file when95 // wins over GOOGLE_APPLICATION_CREDENTIALS and the default ADC file when
86 // multiple credential sources are present simultaneously.96 // multiple credential sources are present simultaneously.
87 func TestResolveCredentialGoogleVertexPrecedence(t *testing.T) {97 func TestResolveCredentialGoogleVertexPrecedence(t *testing.T) {
88- home := t.TempDir()
89 t.Setenv("ZOT_HOME", t.TempDir())98 t.Setenv("ZOT_HOME", t.TempDir())
90- t.Setenv("HOME", home)99+ adcPath := isolateGoogleVertexADC(t)
91 t.Setenv("GOOGLE_CLOUD_API_KEY", "explicit-key")100 t.Setenv("GOOGLE_CLOUD_API_KEY", "explicit-key")
92 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/path/to/sa.json")101 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/path/to/sa.json")
93 102
94- adcDir := filepath.Join(home, ".config", "gcloud")103+ if err := os.MkdirAll(filepath.Dir(adcPath), 0o755); err != nil {
95- if err := os.MkdirAll(adcDir, 0o755); err != nil {
96 t.Fatal(err)104 t.Fatal(err)
97 }105 }
98- adcPath := filepath.Join(adcDir, "application_default_credentials.json")
99 if err := os.WriteFile(adcPath, []byte(`{"type":"authorized_user"}`), 0o600); err != nil {106 if err := os.WriteFile(adcPath, []byte(`{"type":"authorized_user"}`), 0o600); err != nil {
100 t.Fatal(err)107 t.Fatal(err)
101 }108 }
@@ -117,9 +124,8 @@ func TestResolveCredentialGoogleVertexPrecedence(t *testing.T) {
117 // auth.json entry), resolution fails with an error rather than silently124 // auth.json entry), resolution fails with an error rather than silently
118 // returning an empty credential.125 // returning an empty credential.
119 func TestResolveCredentialGoogleVertexNoCredentials(t *testing.T) {126 func TestResolveCredentialGoogleVertexNoCredentials(t *testing.T) {
120- home := t.TempDir()
121 t.Setenv("ZOT_HOME", t.TempDir())127 t.Setenv("ZOT_HOME", t.TempDir())
122- t.Setenv("HOME", home)128+ isolateGoogleVertexADC(t)
123 t.Setenv("GOOGLE_CLOUD_API_KEY", "")129 t.Setenv("GOOGLE_CLOUD_API_KEY", "")
124 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")130 t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")
125 131
A packages/provider/auth/google_adc.go +25 -0
@@ -0,0 +1,25 @@
1+package auth
2+
3+import (
4+ "os"
5+ "path/filepath"
6+ "runtime"
7+)
8+
9+// GoogleApplicationDefaultCredentialsPath returns the default path written by
10+// gcloud auth application-default login.
11+func GoogleApplicationDefaultCredentialsPath() (string, error) {
12+ if runtime.GOOS == "windows" {
13+ configDir, err := os.UserConfigDir()
14+ if err != nil {
15+ return "", err
16+ }
17+ return filepath.Join(configDir, "gcloud", "application_default_credentials.json"), nil
18+ }
19+
20+ home, err := os.UserHomeDir()
21+ if err != nil {
22+ return "", err
23+ }
24+ return filepath.Join(home, ".config", "gcloud", "application_default_credentials.json"), nil
25+}
@@ -0,0 +1,25 @@
1+package auth
2+
3+import (
4+ "os"
5+ "path/filepath"
6+ "runtime"
7+)
8+
9+// GoogleApplicationDefaultCredentialsPath returns the default path written by
10+// gcloud auth application-default login.
11+func GoogleApplicationDefaultCredentialsPath() (string, error) {
12+ if runtime.GOOS == "windows" {
13+ configDir, err := os.UserConfigDir()
14+ if err != nil {
15+ return "", err
16+ }
17+ return filepath.Join(configDir, "gcloud", "application_default_credentials.json"), nil
18+ }
19+
20+ home, err := os.UserHomeDir()
21+ if err != nil {
22+ return "", err
23+ }
24+ return filepath.Join(home, ".config", "gcloud", "application_default_credentials.json"), nil
25+}
A packages/provider/auth/google_adc_test.go +28 -0
@@ -0,0 +1,28 @@
1+package auth
2+
3+import (
4+ "path/filepath"
5+ "runtime"
6+ "testing"
7+)
8+
9+func TestGoogleApplicationDefaultCredentialsPath(t *testing.T) {
10+ root := t.TempDir()
11+ t.Setenv("HOME", root)
12+ t.Setenv("APPDATA", root)
13+
14+ got, err := GoogleApplicationDefaultCredentialsPath()
15+ if err != nil {
16+ t.Fatalf("GoogleApplicationDefaultCredentialsPath: %v", err)
17+ }
18+
19+ var want string
20+ if runtime.GOOS == "windows" {
21+ want = filepath.Join(root, "gcloud", "application_default_credentials.json")
22+ } else {
23+ want = filepath.Join(root, ".config", "gcloud", "application_default_credentials.json")
24+ }
25+ if got != want {
26+ t.Fatalf("path = %q, want %q", got, want)
27+ }
28+}
@@ -0,0 +1,28 @@
1+package auth
2+
3+import (
4+ "path/filepath"
5+ "runtime"
6+ "testing"
7+)
8+
9+func TestGoogleApplicationDefaultCredentialsPath(t *testing.T) {
10+ root := t.TempDir()
11+ t.Setenv("HOME", root)
12+ t.Setenv("APPDATA", root)
13+
14+ got, err := GoogleApplicationDefaultCredentialsPath()
15+ if err != nil {
16+ t.Fatalf("GoogleApplicationDefaultCredentialsPath: %v", err)
17+ }
18+
19+ var want string
20+ if runtime.GOOS == "windows" {
21+ want = filepath.Join(root, "gcloud", "application_default_credentials.json")
22+ } else {
23+ want = filepath.Join(root, ".config", "gcloud", "application_default_credentials.json")
24+ }
25+ if got != want {
26+ t.Fatalf("path = %q, want %q", got, want)
27+ }
28+}
M packages/provider/google_vertex.go +3 -2
@@ -51,6 +51,8 @@ import (
5151 "strings"
5252 "sync"
5353 "time"
54+
55+ providerauth "github.com/patriceckhart/zot/packages/provider/auth"
5456 )
5557
5658 type vertexConfig struct {
@@ -100,8 +102,7 @@ func loadVertexConfig() (*vertexConfig, error) {
100102 credPath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")
101103 if credPath == "" {
102104 // ADC default path. Mirrors `gcloud auth application-default login`.
103- if home, err := os.UserHomeDir(); err == nil {
104- candidate := home + "/.config/gcloud/application_default_credentials.json"
105+ if candidate, err := providerauth.GoogleApplicationDefaultCredentialsPath(); err == nil {
105106 if _, err := os.Stat(candidate); err == nil {
106107 credPath = candidate
107108 }
@@ -51,6 +51,8 @@ import (
51 "strings"51 "strings"
52 "sync"52 "sync"
53 "time"53 "time"
54+
55+ providerauth "github.com/patriceckhart/zot/packages/provider/auth"
54 )56 )
55 57
56 type vertexConfig struct {58 type vertexConfig struct {
@@ -100,8 +102,7 @@ func loadVertexConfig() (*vertexConfig, error) {
100 credPath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")102 credPath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")
101 if credPath == "" {103 if credPath == "" {
102 // ADC default path. Mirrors `gcloud auth application-default login`.104 // ADC default path. Mirrors `gcloud auth application-default login`.
103- if home, err := os.UserHomeDir(); err == nil {105+ if candidate, err := providerauth.GoogleApplicationDefaultCredentialsPath(); err == nil {
104- candidate := home + "/.config/gcloud/application_default_credentials.json"
105 if _, err := os.Stat(candidate); err == nil {106 if _, err := os.Stat(candidate); err == nil {
106 credPath = candidate107 credPath = candidate
107 }108 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close