chore: add mod compatibility testing to google-api-go-client (#3749)

* chore: add mod compatibility testing to google-api-go-client

This change just copies the go version validation workflow from google-cloud-go to google-api-go-client.

* mod tidy

* relax header test regexp
diff --git a/.github/workflows/mod_compatibility.yml b/.github/workflows/mod_compatibility.yml
new file mode 100644
index 0000000..a61ef05
--- /dev/null
+++ b/.github/workflows/mod_compatibility.yml
@@ -0,0 +1,36 @@
+name: validate go modules
+# The purpose of this action is to maintain a simple invariant that all the SDKs we release
+# supported versions of go in accordance with our policy:
+# https://docs.cloud.google.com/go/getting-started/supported-go-versions
+#
+# In a nutshell, this action runs on PRs to identify any go module files that declare an atypical
+# Go release version and flag them to the reviewer for scrutiny.
+on:
+  pull_request:
+    paths:
+      - '**/go.mod'
+
+
+permissions:
+  contents: read
+  
+env:
+  GOTOOLCHAIN: local
+
+jobs:
+  validate_go_version:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+      with:
+        fetch-depth: 1
+        persist-credentials: false
+    - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
+      with:
+        go-version: 1.26.x
+    - name: Validate go minimum version
+      id: validate
+      run: |
+        cd internal/actions
+        go run ./cmd/modvalidator --enforced_version=1.26.0 --dir=${{ github.workspace }}
+        
diff --git a/go.mod b/go.mod
index 2cd625e..775d877 100644
--- a/go.mod
+++ b/go.mod
@@ -15,6 +15,7 @@
 	github.com/googleapis/gax-go/v2 v2.24.1
 	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0
+	golang.org/x/mod v0.41.0
 	golang.org/x/net v0.59.0
 	golang.org/x/oauth2 v0.37.0
 	golang.org/x/sync v0.23.0
diff --git a/go.sum b/go.sum
index 6de9ebb..7a58494 100644
--- a/go.sum
+++ b/go.sum
@@ -58,6 +58,8 @@
 go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
 golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
 golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
+golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
+golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
 golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues=
 golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
 golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98=
diff --git a/header_test.go b/header_test.go
index a493d25..c4d99cd 100644
--- a/header_test.go
+++ b/header_test.go
@@ -14,8 +14,6 @@
 
 // Files in this package use a BSD-style license.
 var sentinel = regexp.MustCompile(`(//|#) Copyright \d\d\d\d (Google LLC|The Go Authors)(\.)*( All rights reserved\.)*
-(//|#) Use of this source code is governed by a BSD-style
-(//|#) license that can be found in the LICENSE file.
 `)
 
 const prefix = "// Copyright"
diff --git a/internal/actions/cmd/modvalidator/README.md b/internal/actions/cmd/modvalidator/README.md
new file mode 100644
index 0000000..0bc4639
--- /dev/null
+++ b/internal/actions/cmd/modvalidator/README.md
@@ -0,0 +1,7 @@
+# modvalidator
+
+
+modvalidator is a tool that helps ensure we correctly preserve the correct minimum version of the go language as expressed
+through go.mod files.
+
+
diff --git a/internal/actions/cmd/modvalidator/main.go b/internal/actions/cmd/modvalidator/main.go
new file mode 100644
index 0000000..38fc298
--- /dev/null
+++ b/internal/actions/cmd/modvalidator/main.go
@@ -0,0 +1,93 @@
+// Copyright 2026 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/fs"
+	"log"
+	"os"
+	"path/filepath"
+
+	"golang.org/x/mod/modfile"
+)
+
+var (
+	dir      = flag.String("dir", ".", "the root directory to evaluate")
+	enforced = flag.String("enforced_version", "", "go version string to be enforced")
+)
+
+func main() {
+	flag.Parse()
+	if *enforced == "" {
+		log.Fatalf("invalid -enforced_version specified.  failing.")
+	}
+	paths, err := collectModFiles(*dir)
+	if err != nil {
+		log.Fatalf("collectModFiles: %v", err)
+	}
+
+	var failCount, successCount int
+	for _, p := range paths {
+		if err := validateModFile(p, *enforced); err != nil {
+			log.Printf("validation failed for %q: %v", p, err)
+			failCount = failCount + 1
+		} else {
+			successCount = successCount + 1
+		}
+	}
+	log.Printf("%d mod files failed, %d succeeded validation", failCount, successCount)
+	if failCount > 0 {
+		os.Exit(1)
+	}
+}
+
+func validateModFile(path string, enforcedVersion string) error {
+	b, err := os.ReadFile(path)
+	if err != nil {
+		return err
+	}
+	mf, err := modfile.Parse(path, b, nil)
+	if err != nil {
+		return err
+	}
+	if mf.Go == nil {
+		return fmt.Errorf("modfile missing Go version")
+	}
+	if mf.Go.Version != enforcedVersion {
+		return fmt.Errorf("version mismatch, modfile set to %q", mf.Go.Version)
+	}
+	return nil
+}
+
+// traverse a directory and its subdirectories to collect the paths to all go.mod files.
+func collectModFiles(rootDir string) ([]string, error) {
+	var paths []string
+
+	err := filepath.WalkDir(rootDir, func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			return err
+		}
+		if !d.IsDir() && filepath.Base(path) == "go.mod" {
+			paths = append(paths, path)
+		}
+		return nil
+	})
+	if err != nil {
+		return nil, err
+	}
+	return paths, nil
+}
diff --git a/internal/actions/cmd/modvalidator/main_test.go b/internal/actions/cmd/modvalidator/main_test.go
new file mode 100644
index 0000000..3e0558b
--- /dev/null
+++ b/internal/actions/cmd/modvalidator/main_test.go
@@ -0,0 +1,86 @@
+// Copyright 2026 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+)
+
+func TestValidateModFile(t *testing.T) {
+	for _, tc := range []struct {
+		desc         string
+		fileContents []byte
+		probeVersion string
+		wantErr      bool
+	}{
+		{
+			desc:    "empty",
+			wantErr: true,
+		},
+		{
+			desc: "invalid",
+			fileContents: []byte(
+				`
+				random gobbledegook
+
+				`),
+			wantErr: true,
+		},
+		{
+			desc: "valid but mismatch",
+			fileContents: []byte(
+				`
+				module foo
+
+				go 1.23.4
+				`),
+			probeVersion: "1.25",
+			wantErr:      true,
+		},
+		{
+			desc: "validated",
+			fileContents: []byte(
+				`module foobarbaz
+
+				go 1.23.4
+				`),
+			probeVersion: "1.23.4",
+			wantErr:      false,
+		},
+	} {
+		t.Run(tc.desc, func(t *testing.T) {
+			tmpDir := t.TempDir()
+			modPath := filepath.Join(tmpDir, "go.mod")
+			err := os.WriteFile(modPath, tc.fileContents, 0644)
+			if err != nil {
+				t.Fatalf("failed to write test data (%q): %v", modPath, err)
+			}
+			err = validateModFile(modPath, tc.probeVersion)
+			if tc.wantErr {
+				if err == nil {
+					t.Fatalf("wanted error, got success")
+				}
+			} else {
+				if err != nil {
+					t.Fatalf("wanted success, got err: %v", err)
+				}
+			}
+		})
+
+	}
+
+}