From e9e7c5f7b5ea7b42134fb04affd889b52ebd58ba Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Sun, 3 Aug 2025 14:12:49 +0200 Subject: [PATCH 1/6] chore(test): fix concurrency problem --- cmd/katenary/main_test.go | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/cmd/katenary/main_test.go b/cmd/katenary/main_test.go index 679fee7..758806b 100644 --- a/cmd/katenary/main_test.go +++ b/cmd/katenary/main_test.go @@ -13,8 +13,7 @@ func TestBuildCommand(t *testing.T) { rootCmd := buildRootCmd() if rootCmd == nil { t.Errorf("Expected rootCmd to be defined") - } - if rootCmd.Use != "katenary" { + } else if rootCmd.Use != "katenary" { t.Errorf("Expected rootCmd.Use to be katenary, got %s", rootCmd.Use) } numCommands := 6 @@ -53,18 +52,27 @@ func TestSchemaCommand(t *testing.T) { } schema := generateSchemaCommand() old := os.Stdout - r, w, _ := os.Pipe() + r, w, err := os.Pipe() + if err != nil { + t.Fatalf("Failed to create pipe: %v", err) + } os.Stdout = w - schema.Run(cmd, nil) - w.Close() - os.Stdout = old var buf bytes.Buffer + + done := make(chan struct{}) + go func() { + schema.Run(cmd, nil) + w.Close() + close(done) + }() io.Copy(&buf, r) - output := buf.String() + <-done + + os.Stdout = old // try to parse json - schemaContent := make(map[string]interface{}) - if err := json.Unmarshal([]byte(output), &schemaContent); err != nil { - t.Errorf("Expected valid json, got %s", output) + schemaContent := make(map[string]any) + if err := json.Unmarshal(buf.Bytes(), &schemaContent); err != nil { + t.Errorf("Expected valid json") } } From 0b3f7b2b5c181219346cd0cd06d5d2be2b157402 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Sun, 3 Aug 2025 14:13:42 +0200 Subject: [PATCH 2/6] chore(code): remove dead code --- generator/service.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/generator/service.go b/generator/service.go index 730577f..c3b4d5e 100644 --- a/generator/service.go +++ b/generator/service.go @@ -51,11 +51,7 @@ func NewService(service types.ServiceConfig, appName string) *Service { // AddPort adds a port to the service. func (s *Service) AddPort(port types.ServicePortConfig, serviceName ...string) { - name := s.service.Name - if len(serviceName) > 0 { - name = serviceName[0] - } - + var name string var finalport intstr.IntOrString if targetPort := utils.GetServiceNameByPort(int(port.Target)); targetPort == "" { From 6aaeda7a3c6c945350754681817251708e103212 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Sun, 3 Aug 2025 14:13:57 +0200 Subject: [PATCH 3/6] chore(modernize): use iterator --- generator/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/service.go b/generator/service.go index c3b4d5e..2070641 100644 --- a/generator/service.go +++ b/generator/service.go @@ -84,7 +84,7 @@ func (s *Service) Yaml() ([]byte, error) { } lines := []string{} - for _, line := range strings.Split(string(y), "\n") { + for line := range strings.SplitSeq(string(y), "\n") { if regexp.MustCompile(`^\s*loadBalancer:\s*`).MatchString(line) { continue } From b4b122fe7f39b11731c2878d9fbb13421ecb5439 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Sun, 3 Aug 2025 14:14:29 +0200 Subject: [PATCH 4/6] Do not ignore cmd/katenary directory --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 88f15a1..0fe6554 100644 --- a/.gitignore +++ b/.gitignore @@ -13,8 +13,6 @@ cover* .config/ */venv -# local binary -./katenary # will be treated later /examples/* @@ -29,4 +27,6 @@ __pycache__ .rpmmacros *.gpg +# local binaries katenary +!cmd/katenary From e9ad85a0acaafa8e3ffe111ef30f01b9bbc30f23 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Sun, 3 Aug 2025 14:15:35 +0200 Subject: [PATCH 5/6] chore(make): Split and enhancements - split Makefile in several sub files - use a separated container for tests - --- Makefile | 404 +----------------- makefiles/build.mk | 88 ++++ makefiles/containers.mk | 47 ++ makefiles/doc.mk | 56 +++ makefiles/gpg.mk | 33 ++ makefiles/packager.mk | 138 ++++++ makefiles/test.mk | 32 ++ oci/builder/Containerfile | 8 + {packaging => oci}/description | 0 {packaging/oci => oci/packager}/Containerfile | 0 10 files changed, 414 insertions(+), 392 deletions(-) create mode 100644 makefiles/build.mk create mode 100644 makefiles/containers.mk create mode 100644 makefiles/doc.mk create mode 100644 makefiles/gpg.mk create mode 100644 makefiles/packager.mk create mode 100644 makefiles/test.mk create mode 100644 oci/builder/Containerfile rename {packaging => oci}/description (100%) rename {packaging/oci => oci/packager}/Containerfile (100%) diff --git a/Makefile b/Makefile index 991ea7a..713f2d4 100644 --- a/Makefile +++ b/Makefile @@ -21,48 +21,10 @@ GOARCH=amd64 CGO_ENABLED=0 PREFIX=~/.local -warn-docker: - @echo -e "\033[1;31mWarning: Docker is not recommended, use Podman instead.\033[0m" - sleep 5 - -# Get the container (Podman is preferred, but docker can be used too. It may failed with Docker.) -# TODO: propose nerdctl -CTN:=$(shell which podman 2>&1 1>/dev/null && echo "podman" || echo "docker") -ifeq ($(CTN),podman) - CTN_USERMAP=--userns=keep-id -else - $(MAKE) warn-docker - CTN_USERMAP=--user=$(shell id -u):$(shell id -g) -e HOME=/tmp -endif - - -# Packaging OCI image, to build rpm, deb, pacman, tar packages -# We changes the keep-id uid/gid for Podman, so that the user inside the container is the same as the user outside. -# For Docker, as it doesn't support userns, we use common options, but it may fail... -PKG_OCI_IMAGE=packaging:fedora -ifeq ($(CTN),podman) - # podman - PKG_OCI_OPTS:=--rm -it \ - -v ./:/opt/katenary:z \ - --userns keep-id:uid=1001,gid=1001 \ - $(PKG_OCI_IMAGE) -else - # docker - PKG_OCI_OPTS:=--rm -it \ - -v ./:/opt/katenary:z \ - -e HOME=/tmp \ - $(CTN_USERMAP) \ - $(PKG_OCI_IMAGE) -endif -GO_BUILD=go build -ldflags="-X 'katenary/generator.Version=$(VERSION)'" -o $(OUTPUT) ./cmd/katenary - - # UPX compression UPX_OPTS = UPX ?= upx $(UPX_OPTS) -BUILD_IMAGE=docker.io/golang:$(GOVERSION) - # List of source files SOURCES=$(shell find -name "*.go" -or -name "*.tpl" -type f | grep -v -P "^./example|^./vendor") # List of binaries to build and sign @@ -84,8 +46,20 @@ SIGNER=metal3d@gmail.com # Browser command to see coverage report after tests BROWSER=$(shell command -v epiphany || echo xdg-open) +include makefiles/build.mk +include makefiles/containers.mk +include makefiles/doc.mk +include makefiles/gpg.mk +include makefiles/packager.mk +include makefiles/test.mk + all: build +# if docker is used instead of podman, we warn the user +warn-docker: + @echo -e "\033[1;31mWarning: Docker is not recommended, use Podman instead.\033[0m" + sleep 5 + help: @cat < Build on host using go" - $(GO_BUILD) -else - @echo "=> Build in container using" $(CTN) - @$(CTN) run \ - -e CGO_ENABLED=$(CGO_ENABLED) \ - -e GOOS=$(GOOS) \ - -e GOARCH=$(GOARCH) \ - --rm -v $(PWD):/go/src/katenary:z \ - -w /go/src/katenary \ - -v go-cache:/go/pkg/mod:z \ - $(CTN_USERMAP) \ - $(BUILD_IMAGE) $(GO_BUILD) -endif - - -# Make dist, build executables for all platforms, sign them, and compress them with upx if possible. -# Also generate the windows installer. -binaries: prepare $(BINARIES) -dist: binaries upx packages -dist-full: clean-dist dist gpg-sign check-sign rpm-sign check-dist-all - -prepare: pull packager-oci-image - mkdir -p dist - -dist/katenary-linux-amd64: $(SOURCES) go.mod go.sum - @echo - @echo -e "\033[1;32mBuilding katenary $(VERSION) for linux-amd64...\033[0m" - $(MAKE) katenary GOOS=linux GOARCH=amd64 OUTPUT=$@ - strip $@ - -dist/katenary-linux-arm64: $(SOURCES) go.mod go.sum - @echo - @echo -e "\033[1;32mBuilding katenary $(VERSION) for linux-arm...\033[0m" - $(MAKE) katenary GOOS=linux GOARCH=arm64 OUTPUT=$@ - -dist/katenary.exe: $(SOURCES) go.mod go.sum - @echo - @echo -e "\033[1;32mBuilding katenary $(VERSION) for windows...\033[0m" - $(MAKE) katenary GOOS=windows GOARCH=amd64 OUTPUT=$@ - -dist/katenary-darwin-amd64: $(SOURCES) go.mod go.sum - @echo - @echo -e "\033[1;32mBuilding katenary $(VERSION) for darwin...\033[0m" - $(MAKE) katenary GOOS=darwin GOARCH=amd64 OUTPUT=$@ - -dist/katenary-freebsd-amd64: $(SOURCES) go.mod go.sum - @echo - @echo -e "\033[1;32mBuilding katenary $(VERSION) for freebsd...\033[0m" - $(MAKE) katenary GOOS=freebsd GOARCH=amd64 OUTPUT=$@ - strip $@ - -dist/katenary-freebsd-arm64: $(SOURCES) go.mod go.sum - @echo - @echo -e "\033[1;32mBuilding katenary $(VERSION) for freebsd-arm64...\033[0m" - $(MAKE) katenary GOOS=freebsd GOARCH=arm64 OUTPUT=$@ - -dist/katenary-windows-setup.exe: nsis/EnVar.dll dist/katenary.exe - @$(CTN) run -w /opt/katenary $(PKG_OCI_OPTS) \ - makensis -DAPP_VERSION=$(VERSION) nsis/katenary.nsi - mv nsis/katenary-windows-setup.exe dist/katenary-windows-setup.exe - -# Download the EnVar plugin for NSIS, put it in the nsis directory, and clean up -nsis/EnVar.dll: - curl https://nsis.sourceforge.io/mediawiki/images/7/7f/EnVar_plugin.zip -o nsis/EnVar_plugin.zip - cd nsis - unzip -o EnVar_plugin.zip Plugins/x86-unicode/EnVar.dll - mv Plugins/x86-unicode/EnVar.dll EnVar.dll - rm -rf EnVar_plugin.zip Plugins - -# UPX compression -upx: upx-linux upx-darwin - -upx-linux: dist/katenary-linux-amd64 dist/katenary-linux-arm64 - $(UPX) $^ - -upx-darwin: dist/katenary-darwin-amd64 - $(UPX) --force-macos $^ - -## Linux / FreeBSD packages with fpm - -DESCRIPTION := $(shell cat packaging/description | sed ':a;N;$$!ba;s/\n/\\n/g') - -FPM_OPTS=--name katenary \ - --url https://katenary.org \ - --vendor "Katenary Project" \ - --maintainer "Patrice Ferlet " \ - --license "MIT" \ - --description="$$(printf "$(DESCRIPTION)" | fold -s)" - -# base files (doc...) -FPM_BASES=../LICENSE=/usr/local/share/doc/katenary/LICENSE \ - ../README.md=/usr/local/share/doc/katenary/README.md - -FPM_COMMON_FILES=$(FPM_BASES) ../doc/share/man/man1/katenary.1=/usr/local/share/man/man1/katenary.1 - -# ArchLinux has got inconsistent /usr/local/man directory -FPM_COMMON_FILES_ARCHLINUX=$(FPM_BASES) ../doc/share/man/man1/katenary.1=/usr/local/man/man1/katenary.1 \ - -# Pacman refuses dashes in version, and should start with a number -PACMAN_VERSION=$(shell echo $(VERSION) | sed 's/-/./g; s/^v//') - -define RPM_MACROS -%_signature gpg -%_gpg_path /home/builder/.gnupg -%_gpg_name $(SIGNER) -%_gpgbin /usr/bin/gpg2 -%__gpg_sign_cmd %{__gpg} gpg --force-v3-sigs --batch --verbose --no-armor --no-secmem-warning -u "%{_gpg_name}" -sbo %{__signature_filename} --digest-algo sha256 %{__plaintext_filename}' -endef - -rpm: dist/katenary-linux-$(GOARCH) - @echo "==> Building RPM packages for $(GOARCH)..." - $(CTN) run -w /opt/katenary/dist $(PKG_OCI_OPTS) \ - fpm -s dir -t rpm -a $(GOARCH) -f $(FPM_OPTS) --version=$(VERSION) \ - $(FPM_COMMON_FILES) \ - ./katenary-linux-$(GOARCH)=/usr/local/bin/katenary -rpm-sign: - [ -f .rpmmacros ] || echo "$(RPM_MACROS)" > .rpmmacros - [ -f .secret.gpg ] || gpg --export-secret-keys -a $(SIGNER) > .secret.gpg - $(CTN) run -w /opt/katenary/dist \ - -v ./.secret.gpg:/home/builder/signer.gpg \ - -v packager-gpg:/home/builder/.gnupg \ - $(PKG_OCI_OPTS) \ - gpg --import /home/builder/signer.gpg - $(CTN) run -w /opt/katenary/dist \ - -v .rpmmacros:/home/builder/.rpmmacros:z \ - -v packager-gpg:/home/builder/.gnupg \ - $(PKG_OCI_OPTS) \ - bash -c 'for rpm in $$(find . -iname "*.rpm"); do echo signing: $$rpm; rpm --addsign $$rpm; done' - -deb: - @echo "==> Building DEB packages for $(GOARCH)..." - $(CTN) run -w /opt/katenary/dist $(PKG_OCI_OPTS) \ - fpm -s dir -t deb -a $(GOARCH) -f $(FPM_OPTS) --version=$(VERSION) \ - $(FPM_COMMON_FILES) \ - ./katenary-linux-$(GOARCH)=/usr/local/bin/katenary - -pacman: - @echo "==> Building Pacman packages for $(GOARCH)..." - $(CTN) run -w /opt/katenary/dist $(PKG_OCI_OPTS) \ - fpm -s dir -t pacman -a $(GOARCH) -f $(FPM_OPTS) --version=$(PACMAN_VERSION) \ - $(FPM_COMMON_FILES_ARCHLINUX) \ - ./katenary-linux-$(GOARCH)=/usr/local/bin/katenary - -freebsd: - @echo "==> Building FreeBSD packages for $(GOARCH)..." - $(CTN) run -w /opt/katenary/dist $(PKG_OCI_OPTS) \ - fpm -s dir -t freebsd -a $(GOARCH) -f $(FPM_OPTS) --version=$(VERSION)\ - $(FPM_COMMON_FILES) \ - ./katenary-freebsd-$(GOARCH)=/usr/local/bin/katenary - mv dist/katenary-$(VERSION).txz dist/katenary-freebsd-$(VERSION).$(GOARCH).txz - -tar: - @echo "==> Building TAR packages for $(GOOS) $(GOARCH)..." - $(CTN) run -w /opt/katenary/dist $(PKG_OCI_OPTS) \ - fpm -s dir -t tar -a $(GOARCH) -f $(FPM_OPTS) \ - $(FPM_COMMON_FILES) \ - ./katenary-$(GOOS)-$(GOARCH)=/usr/local/bin/katenary - mv dist/katenary.tar dist/katenary-$(GOOS)-$(VERSION).$(GOARCH).tar - -packages: manpage packager-oci-image - for arch in amd64 arm64; do \ - $(MAKE) rpm GOARCH=$$arch; \ - $(MAKE) deb GOARCH=$$arch; \ - $(MAKE) pacman GOARCH=$$arch; \ - $(MAKE) freebsd GOARCH=$$arch; \ - $(MAKE) tar GOARCH=$$arch GOOS=linux; \ - $(MAKE) tar GOARCH=$$arch GOOS=freebsd; \ - done - -packager-oci-image: - @$(CTN) build -t packaging:fedora ./packaging/oci 1>/dev/null - -## GPG signing - -gpg-sign: - rm -f dist/*.asc - $(MAKE) $(ASC_BINARIES) - -check-sign: - @echo "=> Checking signatures..." - @for f in $(ASC_BINARIES); do \ - if gpg --verify $$f &>/dev/null; then \ - echo "Signature for $$f is valid"; \ - else \ - echo "Signature for $$f is invalid"; \ - exit 1; \ - fi; \ - done - @echo "=> checking in blank environment..." - keyid=$(shell gpg -k --with-colons $(SIGNER)| grep '^pub' | cut -d: -f5); - $(CTN) run --rm -it -e GPGKEY=$${keyid} -v ./dist:/opt/dist:z \ - packaging:fedora \ - bash -c ' - gpg --recv-key $$GPGKEY || exit 1; - echo "Trusting $(SIGNER) key..."; - echo "trusted-key 483493B2DD0845DA8F21A26DF3702E3FAD8F76DC" >> ~/.gnupg/gpg.conf; - gpg --update-trustdb; - rm -f ~/.gnupg/gpg.conf; - for f in /opt/dist/*.asc; do echo "==> $${f}"; gpg --verify $${f}; done; - echo "=> Listing imported keys..."; - gpg -k - ' - -dist/%.asc: dist/% - gpg --armor --detach-sign --default-key $(SIGNER) $< &>/dev/null || exit 1 - - -check-dist-rocky: - @echo "=> Checking Rocky Linux package..." - p=$(wildcard dist/*x86_64.rpm); - $(CTN) run --rm -it -v ./dist:/opt:z quay.io/rockylinux/rockylinux:latest bash -c " - rpm -ivh /opt/$$(basename $$p); - katenary version; - " - -check-dist-fedora: - @echo "=> Checking Fedora package..." - p=$(wildcard dist/*x86_64.rpm); - $(CTN) run --rm -it -v ./dist:/opt:z quay.io/fedora/fedora:latest bash -c " - rpm -ivh /opt/$$(basename $$p); - katenary version; - " - -check-dist-archlinux: - echo "=> Checking ArchLinux package..." - p=$(wildcard dist/*x86_64.pkg.tar.zst); - $(CTN) run --rm -it -v ./dist:/opt:z quay.io/archlinux/archlinux bash -c " - pacman -U /opt/$$(basename $$p) --noconfirm; - katenary version; - " - -check-dist-debian: - @echo "=> Checking Debian package..." - p=$(wildcard dist/*amd64.deb); - $(CTN) run --rm -it -v ./dist:/opt:z debian:latest bash -c " - dpkg -i /opt/$$(basename $$p); - katenary version; - " -check-dist-ubuntu: - @echo "=> Checking Ubuntu package..." - p=$(wildcard dist/*amd64.deb); - $(CTN) run --rm -it -v ./dist:/opt:z ubuntu:latest bash -c " - dpkg -i /opt/$$(basename $$p); - katenary version; - " - -check-dist-all: - $(MAKE) check-dist-fedora - $(MAKE) check-dist-rocky - $(MAKE) check-dist-debian - $(MAKE) check-dist-ubuntu - $(MAKE) check-dist-archlinux - ## installation and uninstallation install: build @@ -398,93 +105,6 @@ install: build uninstall: rm -f $(PREFIX)/bin/katenary -serve-doc: doc - @cd doc && \ - [ -d venv ] || python -m venv venv; \ - source venv/bin/activate && \ - echo "==> Installing requirements in the virtual env..." - pip install -qq -r requirements.txt && \ - echo "==> Serving doc with mkdocs..." && \ - mkdocs serve - -## Documentation generation - -doc: - @echo "=> Generating documentation..." - # generate the labels doc and code doc - $(MAKE) __label_doc - -manpage: - @echo "=> Generating manpage from documentation" - @cd doc && \ - [ -d venv ] || python -m venv venv; \ - source venv/bin/activate && \ - echo "==> Installing requirements in the virtual env..." && \ - pip install -qq -r requirements.txt && \ - echo "==> Generating manpage..." && \ - MANPAGE=true mkdocs build && \ - rm -rf site && - echo "==> Manpage generated in doc/share/man/man1/katenary.1" - -install-gomarkdoc: - go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest - -__label_doc: - @command -v gomarkdoc || (echo "==> We need to install gomarkdoc..." && \ - $(MAKE) install-gomarkdoc) - @echo "=> Generating labels doc..." - # short label doc - go run ./cmd/katenary help-labels -m | \ - sed -i ' - /START_LABEL_DOC/,/STOP_LABEL_DOC/{/