Skip to content

Commit af40d68

Browse files
committed
windows os support implementation
1 parent 8bce950 commit af40d68

File tree

4 files changed

+134
-18
lines changed

4 files changed

+134
-18
lines changed

pkg/cli/options.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,15 @@ func getPluginsRoot(host string) (pluginsRoot string, err error) {
236236
case "linux":
237237
slog.Debug("Detected host is Linux.")
238238
pluginsRoot = filepath.Join(".config", pluginsRelativePath)
239+
case "windows":
240+
slog.Debug("Detected host is Windows.")
241+
// Use LOCALAPPDATA for Windows plugin storage
242+
localAppData := os.Getenv("LOCALAPPDATA")
243+
if localAppData != "" {
244+
return filepath.Join(localAppData, pluginsRelativePath), nil
245+
}
246+
// Fallback to user home directory if LOCALAPPDATA is not set
247+
pluginsRoot = filepath.Join("AppData", "Local", pluginsRelativePath)
239248
}
240249

241250
userHomeDir, err := os.UserHomeDir()

pkg/cli/options_test.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,43 @@ var _ = Describe("Discover external plugins", func() {
8080
Expect(plgPath).To(Equal(fmt.Sprintf("%s/.config/kubebuilder/plugins", homePath)))
8181
})
8282

83-
It("should return error when the host is not darwin / linux", func() {
83+
// It("should return error when the host is not darwin / linux", func() {
84+
It("should return the correct path for the windows OS with LOCALAPPDATA", func() {
85+
originalLocalAppData := os.Getenv("LOCALAPPDATA")
86+
defer func() {
87+
if originalLocalAppData != "" {
88+
os.Setenv("LOCALAPPDATA", originalLocalAppData)
89+
} else {
90+
os.Unsetenv("LOCALAPPDATA")
91+
}
92+
}()
93+
94+
testPath := filepath.Join("C:", "Users", "TestUser", "AppData", "Local")
95+
err := os.Setenv("LOCALAPPDATA", testPath)
96+
Expect(err).ToNot(HaveOccurred())
97+
98+
plgPath, err := getPluginsRoot("windows")
99+
Expect(err).ToNot(HaveOccurred())
100+
Expect(plgPath).To(Equal(filepath.Join(testPath, "kubebuilder", "plugins")))
101+
})
102+
103+
It("should return the correct path for the windows OS without LOCALAPPDATA", func() {
104+
originalLocalAppData := os.Getenv("LOCALAPPDATA")
105+
defer func() {
106+
if originalLocalAppData != "" {
107+
os.Setenv("LOCALAPPDATA", originalLocalAppData)
108+
}
109+
}()
110+
111+
err := os.Unsetenv("LOCALAPPDATA")
112+
Expect(err).ToNot(HaveOccurred())
113+
114+
plgPath, err := getPluginsRoot("windows")
115+
Expect(err).ToNot(HaveOccurred())
116+
Expect(plgPath).To(ContainSubstring(filepath.Join("AppData", "Local", "kubebuilder", "plugins")))
117+
})
118+
119+
It("should return error when the host is not darwin / linux / windows", func() {
84120
plgPath, err := getPluginsRoot("random")
85121
Expect(plgPath).To(Equal(""))
86122
Expect(err).To(HaveOccurred())
@@ -122,7 +158,14 @@ var _ = Describe("Discover external plugins", func() {
122158
Expect(plgPath).To(Equal(fmt.Sprintf("%s/kubebuilder/plugins", xdghome)))
123159
})
124160

125-
It("should return error when the host is not darwin / linux", func() {
161+
// It("should return error when the host is not darwin / linux", func() {
162+
It("should return the user given path for windows OS", func() {
163+
plgPath, err := getPluginsRoot("windows")
164+
Expect(plgPath).To(Equal(customPath))
165+
Expect(err).ToNot(HaveOccurred())
166+
})
167+
168+
It("should report error when the host is not darwin / linux / windows", func() {
126169
plgPath, err := getPluginsRoot("random")
127170
Expect(plgPath).To(Equal(""))
128171
Expect(err).To(HaveOccurred())

pkg/cli/root.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ const (
2929
projectVersionsHeader = "Supported project versions"
3030
)
3131

32-
var supportedPlatforms = []string{"darwin", "linux"}
32+
// var supportedPlatforms = []string{"darwin", "linux"}
33+
var supportedPlatforms = []string{"darwin", "linux", "windows"}
34+
3335

3436
func (c CLI) newRootCmd() *cobra.Command {
3537
cmd := &cobra.Command{

pkg/plugins/golang/v4/scaffolds/internal/templates/makefile.go

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,14 @@ help: ## Display this help.
118118
119119
.PHONY: manifests
120120
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
121-
"$(CONTROLLER_GEN)" rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
121+
"$(CONTROLLER_GEN)" rbac:roleName=manager-role crd webhook paths="$(CONTROLLER_GEN_PATHS)" output:crd:artifacts:config=config/crd/bases
122122
123123
.PHONY: generate
124124
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
125125
{{ if .BoilerplatePath -}}
126-
"$(CONTROLLER_GEN)" object:headerFile={{printf "%q" .BoilerplatePath}} paths="./..."
126+
"$(CONTROLLER_GEN)" object:headerFile={{printf "%q" .BoilerplatePath}} paths="$(CONTROLLER_GEN_PATHS)"
127127
{{- else -}}
128-
"$(CONTROLLER_GEN)" object paths="./..."
128+
"$(CONTROLLER_GEN)" object paths="$(CONTROLLER_GEN_PATHS)"
129129
{{- end }}
130130
131131
.PHONY: fmt
@@ -253,17 +253,72 @@ undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.
253253
##@ Dependencies
254254
255255
## Location to install dependencies to
256-
LOCALBIN ?= $(shell pwd)/bin
256+
LOCALBIN ?= $(CURDIR)/bin
257+
258+
# WINDOWS_CYGWIN_FIX: Convert Cygwin/Git Bash paths to Windows paths for Go tools
259+
# This is necessary because:
260+
# - Cygwin make uses /cygdrive/c/... paths (CYGWIN_NT)
261+
# - Git Bash (MINGW64) uses /c/... paths (MINGW64_NT) but make might use /cygdrive/c/...
262+
# - Windows Go expects C:/... paths
263+
# We handle both formats with two sed expressions
264+
define convert-to-windows-path
265+
$(shell echo "$(1)" | sed -e 's|^/cygdrive/\([a-z]\)|\1:|' -e 's|^/\([a-z]\)/|\1:/|' 2>/dev/null || echo "$(1)")
266+
endef
267+
268+
# WINDOWS_CYGWIN_FIX: Convert Windows path to MINGW/Cygwin path for shell commands
269+
# In MINGW64, we need /d/path format, not /cygdrive/d/path
270+
define convert-to-mingw-path
271+
$(shell echo "$(1)" | sed -e 's|^/cygdrive/\([a-z]\)|\1:|' -e 's|^\([a-z]\):|/\1|' 2>/dev/null || echo "$(1)")
272+
endef
273+
274+
# WINDOWS_CYGWIN_FIX: Detect if we're in MINGW/Cygwin environment
275+
UNAME_S := $(shell uname -s 2>/dev/null || echo "")
276+
IS_MINGW := $(shell echo "$(UNAME_S)" | grep -qi mingw && echo "yes" || echo "")
277+
IS_CYGWIN := $(shell echo "$(UNAME_S)" | grep -qi cygwin && echo "yes" || echo "")
278+
279+
LOCALBIN_FOR_GO := $(call convert-to-windows-path,$(LOCALBIN))
280+
281+
# WINDOWS_CYGWIN_FIX: For MINGW64, convert paths for shell commands
282+
ifeq ($(IS_MINGW),yes)
283+
LOCALBIN_FOR_SHELL := $(call convert-to-mingw-path,$(LOCALBIN))
284+
else
285+
LOCALBIN_FOR_SHELL := $(LOCALBIN)
286+
endif
287+
257288
$(LOCALBIN):
258289
mkdir -p "$(LOCALBIN)"
259290
260291
## Tool Binaries
261292
KUBECTL ?= kubectl
262293
KIND ?= kind
294+
# WINDOWS_CYGWIN_FIX: Add .exe extension on Windows and use correct path format
295+
ifeq ($(OS),Windows_NT)
296+
KUSTOMIZE ?= $(LOCALBIN_FOR_SHELL)/kustomize.exe
297+
CONTROLLER_GEN ?= $(LOCALBIN_FOR_SHELL)/controller-gen.exe
298+
ENVTEST ?= $(LOCALBIN_FOR_SHELL)/setup-envtest.exe
299+
GOLANGCI_LINT = $(LOCALBIN_FOR_SHELL)/golangci-lint.exe
300+
else ifeq ($(IS_MINGW),yes)
301+
KUSTOMIZE ?= $(LOCALBIN_FOR_SHELL)/kustomize.exe
302+
CONTROLLER_GEN ?= $(LOCALBIN_FOR_SHELL)/controller-gen.exe
303+
ENVTEST ?= $(LOCALBIN_FOR_SHELL)/setup-envtest.exe
304+
GOLANGCI_LINT = $(LOCALBIN_FOR_SHELL)/golangci-lint.exe
305+
else ifeq ($(IS_CYGWIN),yes)
306+
KUSTOMIZE ?= $(LOCALBIN)/kustomize.exe
307+
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen.exe
308+
ENVTEST ?= $(LOCALBIN)/setup-envtest.exe
309+
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint.exe
310+
else
263311
KUSTOMIZE ?= $(LOCALBIN)/kustomize
264312
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
265313
ENVTEST ?= $(LOCALBIN)/setup-envtest
266314
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
315+
endif
316+
317+
# WINDOWS_CYGWIN_FIX: Get Go module name for controller-gen paths
318+
# On Windows/Cygwin, controller-gen cannot resolve relative paths like "./..." correctly
319+
# because it's a Windows binary running in a Cygwin environment. We use the module name instead.
320+
GO_MODULE := $(shell go list -m 2>/dev/null || echo "")
321+
CONTROLLER_GEN_PATHS := $(if $(GO_MODULE),$(GO_MODULE)/...,./...)
267322
268323
## Tool Versions
269324
KUSTOMIZE_VERSION ?= {{ .KustomizeVersion }}
@@ -283,12 +338,12 @@ GOLANGCI_LINT_VERSION ?= {{ .GolangciLintVersion }}
283338
.PHONY: kustomize
284339
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
285340
$(KUSTOMIZE): $(LOCALBIN)
286-
$(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION))
341+
@$(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION))
287342
288343
.PHONY: controller-gen
289344
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
290345
$(CONTROLLER_GEN): $(LOCALBIN)
291-
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION))
346+
@$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION))
292347
293348
.PHONY: setup-envtest
294349
setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory.
@@ -301,27 +356,34 @@ setup-envtest: envtest ## Download the binaries required for ENVTEST in the loca
301356
.PHONY: envtest
302357
envtest: $(ENVTEST) ## Download setup-envtest locally if necessary.
303358
$(ENVTEST): $(LOCALBIN)
304-
$(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION))
359+
@$(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION))
305360
306361
.PHONY: golangci-lint
307362
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
308363
$(GOLANGCI_LINT): $(LOCALBIN)
309-
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
364+
@$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
310365
311366
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
312-
# $1 - target path with name of binary
367+
# $1 - target path with name of binary (may include .exe extension in MINGW/Cygwin)
313368
# $2 - package url which can be installed
314369
# $3 - specific version of package
315370
define go-install-tool
316-
@[ -f "$(1)-$(3)" ] && [ "$$(readlink -- "$(1)" 2>/dev/null)" = "$(1)-$(3)" ] || { \
371+
@[ -f "$(1)-$(3)" ] || [ -f "$(1)-$(3).exe" ] || { \
317372
set -e; \
318373
package=$(2)@$(3) ;\
319374
echo "Downloading $${package}" ;\
320-
rm -f "$(1)" ;\
321-
GOBIN="$(LOCALBIN)" go install $${package} ;\
322-
mv "$(LOCALBIN)/$$(basename "$(1)")" "$(1)-$(3)" ;\
323-
} ;\
324-
ln -sf "$$(realpath "$(1)-$(3)")" "$(1)"
375+
rm -f "$(1)" "$(1).exe" "$(1)-$(3)" "$(1)-$(3).exe" ;\
376+
GOBIN="$(LOCALBIN_FOR_GO)" go install $${package} ;\
377+
binary_name=$$(basename "$(1)" .exe) ;\
378+
bin_dir=$$(dirname "$(1)") ;\
379+
if [ -f "$${bin_dir}/$${binary_name}.exe" ]; then \
380+
mv "$${bin_dir}/$${binary_name}.exe" "$${bin_dir}/$${binary_name}-$(3).exe" ;\
381+
cp "$${bin_dir}/$${binary_name}-$(3).exe" "$${bin_dir}/$${binary_name}.exe" ;\
382+
elif [ -f "$${bin_dir}/$${binary_name}" ]; then \
383+
mv "$${bin_dir}/$${binary_name}" "$${bin_dir}/$${binary_name}-$(3)" ;\
384+
ln -sf "$${binary_name}-$(3)" "$${bin_dir}/$${binary_name}" 2>/dev/null || cp "$${bin_dir}/$${binary_name}-$(3)" "$${bin_dir}/$${binary_name}" ;\
385+
fi ;\
386+
}
325387
endef
326388
327389
define gomodver

0 commit comments

Comments
 (0)