Skip to content

codegen tool: resolves GOBIN via go env #132378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

bartoszmajsak
Copy link

@bartoszmajsak bartoszmajsak commented Jun 18, 2025

What type of PR is this?

/kind bug

What this PR does / why we need it:

Using go env GOBIN to locate where codegen binaries should be installed provides a more reliable and flexible solution.

  • Picks up a user's custom GOBIN whether it's set as an environment variable or only in go env
  • Falls back to $GOPATH/bin if no GOBIN is defined

This handles setups (e.g. using tools like mise) where GOBIN is configured internally but not exported.

The old lookup missed those internally configured values and could fail if the binary isn't present in the assumed location (or worse invoke the wrong codegen binary :)).

Which issue(s) this PR is related to:

Fixes #132377

Does this PR introduce a user-facing change?

NONE

@k8s-ci-robot k8s-ci-robot added release-note-none Denotes a PR that doesn't merit a release note. size/S Denotes a PR that changes 10-29 lines, ignoring generated files. kind/bug Categorizes issue or PR as related to a bug. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. do-not-merge/needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. labels Jun 18, 2025
@k8s-ci-robot
Copy link
Contributor

This issue is currently awaiting triage.

If a SIG or subproject determines this is a relevant issue, they will accept it by applying the triage/accepted label and provide further guidance.

The triage/accepted label can be added by org members by writing /triage accepted in a comment.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@k8s-ci-robot
Copy link
Contributor

Welcome @bartoszmajsak!

It looks like this is your first PR to kubernetes/kubernetes 🎉. Please refer to our pull request process documentation to help your PR have a smooth ride to approval.

You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. Here is the bot commands documentation.

You can also check if kubernetes/kubernetes has its own contribution guidelines.

You may want to refer to our testing guide if you run into trouble with your tests not passing.

If you are having difficulty getting your pull request seen, please follow the recommended escalation practices. Also, for tips and tricks in the contribution process you may want to read the Kubernetes contributor cheat sheet. We want to make sure your contribution gets all the attention it needs!

Thank you, and welcome to Kubernetes. 😃

@k8s-ci-robot k8s-ci-robot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Jun 18, 2025
@k8s-ci-robot
Copy link
Contributor

Hi @bartoszmajsak. Thanks for your PR.

I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@k8s-ci-robot k8s-ci-robot added needs-priority Indicates a PR lacks a `priority/foo` label and requires one. area/code-generation sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. and removed do-not-merge/needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. labels Jun 18, 2025
@k8s-ci-robot k8s-ci-robot requested review from sttts and wojtek-t June 18, 2025 19:34
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: bartoszmajsak
Once this PR has been reviewed and has the lgtm label, please assign deads2k for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@dims
Copy link
Member

dims commented Jun 20, 2025

/ok-to-test

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Jun 20, 2025
Using `go env GOBIN` to locate where codegen binaries should be installed
provides a more reliable and flexible solution.

- Picks up a user's custom `GOBIN` whether it's set as an environment
  variable or only in `go env`
- Falls back to `$GOPATH/bin` if no `GOBIN` is defined

This handles setups (e.g. using tools like [`mise`](https://mise.jdx.dev/lang/go.html))
where `GOBIN` is configured internally but not exported.

The old lookup missed those internally-configured values and
could invoke the wrong codegen binary.

Signed-off-by: Bartosz Majsak <[email protected]>
Copy link
Member

@BenTheElder BenTheElder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/hold

We expect GOBIN to be setup by kube::golang::setup_env, not sure why that isn't being used here

/assign @thockin

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jun 23, 2025
@@ -116,7 +116,8 @@ function kube::codegen::gen_helpers() {
# shellcheck disable=2046 # printf word-splitting is intentional
GO111MODULE=on go install $(printf "k8s.io/code-generator/cmd/%s " "${BINS[@]}")
)
# Go installs in $GOBIN if defined, and $GOPATH/bin otherwise

GOBIN=$(go env GOBIN) # exported $GOBIN variable takes precedence, otherwise the local go env is taken
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not at a computer to test, but doesn't this mean GOBIN is always defined, making the next line a no-op?

Copy link
Author

@bartoszmajsak bartoszmajsak Jun 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my original assumption as well, after reading go help install

Executables are installed in the directory named by the GOBIN environment
variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH
environment variable is not set. Executables in $GOROOT
are installed in $GOROOT/bin or $GOTOOLDIR instead of $GOBIN.

but it failed kubernetes-verify job. I think the problem boils down to the fact how we execute installed binaries.

The problem is that when unset GOBIN and go env -w GOBIN= are invoked, go install will perform this fallback logic, but it is not exposed in a way we could use it (at least, I do not know how to get it easily, as it's not set back to go env GOBIN).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant is this:

$ set -o nounset

$ unset GOBIN

$ declare -p GOBIN
bash: declare: GOBIN: not found

$ echo "${GOBIN}"
bash: GOBIN: unbound variable

It is unambiguously NOT set. But:

$ GOBIN=$(go env GOBIN)

$ declare -p GOBIN
declare -- GOBIN=""

$ echo "${GOBIN:-unset}"
unset

$ echo "${GOBIN}"

$ set -o nounset

$ unset GOBIN

$ declare -p GOBIN
bash: declare: GOBIN: not found

$ echo "${GOBIN}"
bash: GOBIN: unbound variable

$ GOBIN=$(go env GOBIN)

$ declare -p GOBIN
declare -- GOBIN=""

$ echo "${GOBIN:-unset}"
unset

$ echo "${GOBIN}"


It is now set (as per the shell) to the value "".  The next line still works because `${VAR:-alt}` handles unset or null ("" counts as null, as defined by bash).

Still it makes me uncomfortable because it is too subtle.  What if we go even more explicit.  I tried this quickly, can you cross-check?

```diff
diff --git a/staging/src/k8s.io/code-generator/kube_codegen.sh b/staging/src/k8s.io/code-generator/kube_codegen.sh
index 478ddde11a6..f7907594c94 100755
--- a/staging/src/k8s.io/code-generator/kube_codegen.sh
+++ b/staging/src/k8s.io/code-generator/kube_codegen.sh
@@ -32,6 +32,19 @@ KUBE_CODEGEN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
 # before sourcing this file.
 CODEGEN_VERSION_SPEC="${KUBE_CODEGEN_TAG:+"@${KUBE_CODEGEN_TAG}"}"
 
+# Go installs in $GOBIN if defined, and $GOPATH/bin otherwise. We want to know
+# which one it is, so we can use it later.
+function get_gobin() {
+    local from_env
+    from_env="$(go env GOBIN)"
+    if [[ -z "${from_env}" ]]; then
+        echo "${from_env}"
+    fi
+    echo "$(go env GOPATH)/bin"
+}
+GOBIN="$(get_gobin)"
+export GOBIN
+
 function kube::codegen::internal::findz() {
     # We use `find` rather than `git ls-files` because sometimes external
     # projects use this across repos.  This is an imperfect wrapper of find,
@@ -116,8 +129,6 @@ function kube::codegen::gen_helpers() {
         # shellcheck disable=2046 # printf word-splitting is intentional
         GO111MODULE=on go install $(printf "k8s.io/code-generator/cmd/%s " "${BINS[@]}")
     )
-    # Go installs in $GOBIN if defined, and $GOPATH/bin otherwise
-    gobin="${GOBIN:-$(go env GOPATH)/bin}"
 
     # Deepcopy
     #
@@ -144,7 +155,7 @@ function kube::codegen::gen_helpers() {
             -name zz_generated.deepcopy.go \
             | xargs -0 rm -f
 
-        "${gobin}/deepcopy-gen" \
+        "${GOBIN}/deepcopy-gen" \
             -v "${v}" \
             --output-file zz_generated.deepcopy.go \
             --go-header-file "${boilerplate}" \
@@ -176,7 +187,7 @@ function kube::codegen::gen_helpers() {
             -name zz_generated.validations.go \
             | xargs -0 rm -f
 
-        "${gobin}/validation-gen" \
+        "${GOBIN}/validation-gen" \
             -v "${v}" \
             --output-file zz_generated.validations.go \
             --go-header-file "${boilerplate}" \
@@ -208,7 +219,7 @@ function kube::codegen::gen_helpers() {
             -name zz_generated.defaults.go \
             | xargs -0 rm -f
 
-        "${gobin}/defaulter-gen" \
+        "${GOBIN}/defaulter-gen" \
             -v "${v}" \
             --output-file zz_generated.defaults.go \
             --go-header-file "${boilerplate}" \
@@ -244,7 +255,7 @@ function kube::codegen::gen_helpers() {
         for arg in "${extra_peers[@]:+"${extra_peers[@]}"}"; do
             extra_peer_args+=("--extra-peer-dirs" "$arg")
         done
-        "${gobin}/conversion-gen" \
+        "${GOBIN}/conversion-gen" \
             -v "${v}" \
             --output-file zz_generated.conversion.go \
             --go-header-file "${boilerplate}" \
@@ -367,8 +378,6 @@ function kube::codegen::gen_openapi() {
         # shellcheck disable=2046 # printf word-splitting is intentional
         GO111MODULE=on go install $(printf "k8s.io/kube-openapi/cmd/%s " "${BINS[@]}")
     )
-    # Go installs in $GOBIN if defined, and $GOPATH/bin otherwise
-    gobin="${GOBIN:-$(go env GOPATH)/bin}"
 
     local input_pkgs=( "${extra_pkgs[@]:+"${extra_pkgs[@]}"}")
     while read -r dir; do
@@ -393,7 +402,7 @@ function kube::codegen::gen_openapi() {
             -name zz_generated.openapi.go \
             | xargs -0 rm -f
 
-        "${gobin}/openapi-gen" \
+        "${GOBIN}/openapi-gen" \
             -v "${v}" \
             --output-file zz_generated.openapi.go \
             --go-header-file "${boilerplate}" \
@@ -600,8 +609,6 @@ function kube::codegen::gen_client() {
         # shellcheck disable=2046 # printf word-splitting is intentional
         GO111MODULE=on go install $(printf "k8s.io/code-generator/cmd/%s " "${BINS[@]}")
     )
-    # Go installs in $GOBIN if defined, and $GOPATH/bin otherwise
-    gobin="${GOBIN:-$(go env GOPATH)/bin}"
 
     local group_versions=()
     local input_pkgs=()
@@ -642,7 +649,7 @@ function kube::codegen::gen_client() {
             || true \
         ) | xargs -0 rm -f
 
-        "${gobin}/applyconfiguration-gen" \
+        "${GOBIN}/applyconfiguration-gen" \
             -v "${v}" \
             --go-header-file "${boilerplate}" \
             --output-dir "${out_dir}/${applyconfig_subdir}" \
@@ -665,7 +672,7 @@ function kube::codegen::gen_client() {
     for arg in "${group_versions[@]}"; do
         inputs+=("--input" "$arg")
     done
-     "${gobin}/client-gen" \
+     "${GOBIN}/client-gen" \
         -v "${v}" \
         --go-header-file "${boilerplate}" \
         --output-dir "${out_dir}/${clientset_subdir}" \
@@ -687,7 +694,7 @@ function kube::codegen::gen_client() {
             || true \
         ) | xargs -0 rm -f
 
-        "${gobin}/lister-gen" \
+        "${GOBIN}/lister-gen" \
             -v "${v}" \
             --go-header-file "${boilerplate}" \
             --output-dir "${out_dir}/${listers_subdir}" \
@@ -704,7 +711,7 @@ function kube::codegen::gen_client() {
             || true \
         ) | xargs -0 rm -f
 
-        "${gobin}/informer-gen" \
+        "${GOBIN}/informer-gen" \
             -v "${v}" \
             --go-header-file "${boilerplate}" \
             --output-dir "${out_dir}/${informers_subdir}" \
@@ -772,8 +779,6 @@ function kube::codegen::gen_register() {
         # shellcheck disable=2046 # printf word-splitting is intentional
         GO111MODULE=on go install $(printf "k8s.io/code-generator/cmd/%s " "${BINS[@]}")
     )
-    # Go installs in $GOBIN if defined, and $GOPATH/bin otherwise
-    gobin="${GOBIN:-$(go env GOPATH)/bin}"
 
     # Register
     #
@@ -800,7 +805,7 @@ function kube::codegen::gen_register() {
             -name zz_generated.register.go \
             | xargs -0 rm -f
 
-        "${gobin}/register-gen" \
+        "${GOBIN}/register-gen" \
             -v "${v}" \
             --output-file zz_generated.register.go \
             --go-header-file "${boilerplate}" \

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/code-generation cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. kind/bug Categorizes issue or PR as related to a bug. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. release-note-none Denotes a PR that doesn't merit a release note. sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. size/S Denotes a PR that changes 10-29 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

kube_codegen.sh does not resolve "local" GOBIN when invoking the tools
5 participants