commit ffca4cbf52832ec256c8b50c9e2808bfa3369e5f (tree)
parent 76c7f9b81f75e12398af587813a7a17a6e08a299
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Tue, 21 Oct 2025 11:15:01 +0000
add gcloud-wrapper
Diffstat:
5 files changed, 167 insertions(+), 1 deletion(-)
diff --git a/flake.nix b/flake.nix
@@ -106,6 +106,7 @@
tmuxbash = super.callPackage ./pkgs/tmuxbash.nix { };
vanta-agent = super.callPackage ./pkgs/vanta-agent.nix { };
chronoctl = super.callPackage ./pkgs/chronoctl.nix { };
+ gcloud-wrapper = super.callPackage ./pkgs/gcloud-wrapper { };
pkgs-unstable = import nixpkgs-unstable {
inherit (super) system;
diff --git a/pkgs/gcloud-wrapper/default.nix b/pkgs/gcloud-wrapper/default.nix
@@ -0,0 +1,6 @@
+{ buildGoModule }:
+buildGoModule {
+ name = "gcloud-wrapper";
+ src = ./.;
+ vendorHash = null;
+}
diff --git a/pkgs/gcloud-wrapper/go.mod b/pkgs/gcloud-wrapper/go.mod
@@ -0,0 +1,3 @@
+module git.jakstys.lt/motiejus/config/pkgs/gcloud-wrapper
+
+go 1.23
diff --git a/pkgs/gcloud-wrapper/main.go b/pkgs/gcloud-wrapper/main.go
@@ -0,0 +1,139 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "time"
+)
+
+const (
+ cachePath = ".config/gcloud/config-helper-cache.json"
+ cacheThreshold = 10 * time.Minute
+)
+
+type credentialCache struct {
+ Credential struct {
+ TokenExpiry string `json:"token_expiry"`
+ } `json:"credential"`
+}
+
+func getCachePath() (string, error) {
+ home, err := os.UserHomeDir()
+ if err != nil {
+ return "", err
+ }
+ return filepath.Join(home, cachePath), nil
+}
+
+func argsMatch(args []string) bool {
+ return len(args) == 4 &&
+ args[0] == "config" &&
+ args[1] == "config-helper" &&
+ args[2] == "--format" &&
+ args[3] == "json"
+}
+
+func parseISO8601(s string) (time.Time, error) {
+ return time.Parse(time.RFC3339, s)
+}
+
+func execGcloud(args []string) {
+ argv := make([]string, len(args))
+ argv[0] = "gcloud-wrapped"
+ copy(argv[1:], args[1:])
+
+ cmd := exec.Command(argv[0], argv[1:]...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.Stdin = os.Stdin
+
+ if err := cmd.Run(); err != nil {
+ if exitErr, ok := err.(*exec.ExitError); ok {
+ os.Exit(exitErr.ExitCode())
+ }
+ os.Exit(1)
+ }
+ os.Exit(0)
+}
+
+func runGcloudAndCache(cachePath string) error {
+ cmd := exec.Command("gcloud-wrapped", "config", "config-helper", "--format", "json")
+ output, err := cmd.CombinedOutput()
+
+ if err != nil {
+ os.Remove(cachePath)
+ os.Stderr.Write(output)
+ if exitErr, ok := err.(*exec.ExitError); ok {
+ os.Exit(exitErr.ExitCode())
+ }
+ os.Exit(1)
+ }
+
+ if err := os.MkdirAll(filepath.Dir(cachePath), 0755); err != nil {
+ return err
+ }
+
+ if err := os.WriteFile(cachePath, output, 0600); err != nil {
+ return err
+ }
+
+ os.Stdout.Write(output)
+ return nil
+}
+
+func main() {
+ args := os.Args[1:]
+
+ if !argsMatch(args) {
+ execGcloud(os.Args)
+ }
+
+ cachePath, err := getCachePath()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "failed to get cache path: %v\n", err)
+ os.Exit(1)
+ }
+
+ cacheData, err := os.ReadFile(cachePath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ if err := runGcloudAndCache(cachePath); err != nil {
+ fmt.Fprintf(os.Stderr, "failed to run gcloud: %v\n", err)
+ os.Exit(1)
+ }
+ return
+ }
+ fmt.Fprintf(os.Stderr, "failed to read cache: %v\n", err)
+ os.Exit(1)
+ }
+
+ var cache credentialCache
+ if err := json.Unmarshal(cacheData, &cache); err != nil {
+ if err := runGcloudAndCache(cachePath); err != nil {
+ fmt.Fprintf(os.Stderr, "failed to run gcloud: %v\n", err)
+ os.Exit(1)
+ }
+ return
+ }
+
+ expiry, err := parseISO8601(cache.Credential.TokenExpiry)
+ if err != nil {
+ if err := runGcloudAndCache(cachePath); err != nil {
+ fmt.Fprintf(os.Stderr, "failed to run gcloud: %v\n", err)
+ os.Exit(1)
+ }
+ return
+ }
+
+ if time.Until(expiry) > cacheThreshold {
+ os.Stdout.Write(cacheData)
+ } else {
+ if err := runGcloudAndCache(cachePath); err != nil {
+ fmt.Fprintf(os.Stderr, "failed to run gcloud: %v\n", err)
+ os.Exit(1)
+ }
+ }
+}
diff --git a/shared/work/default.nix b/shared/work/default.nix
@@ -1,4 +1,21 @@
{ config, pkgs, ... }:
+let
+ gcloud-wrapped = pkgs.symlinkJoin {
+ name = "google-cloud-sdk-wrapped";
+ paths = [ pkgs.google-cloud-sdk ];
+ nativeBuildInputs = [ pkgs.makeWrapper ];
+ postBuild = ''
+ # Remove the original gcloud symlink
+ rm $out/bin/gcloud
+
+ # Create a shell wrapper called gcloud-wrapped that executes the real gcloud
+ makeWrapper ${pkgs.google-cloud-sdk}/bin/gcloud $out/bin/gcloud-wrapped
+
+ # Replace gcloud with our caching wrapper
+ ln -s ${pkgs.gcloud-wrapper}/bin/gcloud-wrapper $out/bin/gcloud
+ '';
+ };
+in
{
mj.base.users.email = null;
@@ -19,7 +36,7 @@
github-cli
claude-code
docker-compose
- google-cloud-sdk
+ gcloud-wrapped
kubectl-node-shell
liburing.dev