diff --git a/.gitignore b/.gitignore index 407bccc2143..9b8db2b8ae0 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,9 @@ pkg/cloud/dashboard/Cargo.lock docs/site/node_modules docs/site/package-lock.json +.vscode + +# tools is responsible for collecting sealer third party package binary tools +tools/ +#coverage.out +tmp/ diff --git a/Makefile b/Makefile index 399df64f453..71ebb03e982 100644 --- a/Makefile +++ b/Makefile @@ -1,140 +1,171 @@ +# Copyright © 2022 Alibaba Group Holding Ltd. +# +# 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. + +# ============================================================================== +# define the default goal +# + .DEFAULT_GOAL := help -Dirs=$(shell ls) -GIT_TAG := $(shell git describe --exact-match --tags --abbrev=0 2> /dev/null || echo untagged) -GOOS ?= $(shell go env GOOS) -GOARCH ?= $(shell go env GOARCH) +.PHONY: all +all: tidy gen add-copyright format lint cover build + +# ============================================================================== +# Build set + +ROOT_PACKAGE=github.com/sealerio/sealer +VERSION_PACKAGE=github.com/sealerio/sealer/pkg/version + +BUILD_SCRIPTS := scripts/build.sh + +# ============================================================================== +# Includes + +include scripts/make-rules/common.mk # make sure include common.mk at the first include line +include scripts/make-rules/golang.mk +include scripts/make-rules/image.mk +include scripts/make-rules/copyright.mk +include scripts/make-rules/gen.mk +include scripts/make-rules/dependencies.mk +include scripts/make-rules/tools.mk + +# ============================================================================== +# Usage + +define USAGE_OPTIONS + +Options: + + DEBUG Whether or not to generate debug symbols. Default is 0. + + BINS Binaries to build. Default is all binaries under cmd. + This option is available when using: make {build}(.multiarch) + Example: make build BINS="sealer sealctl" + + PLATFORMS Platform to build for. Default is linux_arm64 and linux_amd64. + This option is available when using: make {build}.multiarch + Example: make build.multiarch PLATFORMS="linux_arm64 linux_amd64" + + V Set to 1 enable verbose build. Default is 0. +endef +export USAGE_OPTIONS + +# ============================================================================== +# Targets + +## build: Build binaries by default +.PHONY: build +build: + @$(MAKE) go.build -TOOLS_DIR := hack/build.sh +## tidy: tidy go.mod +.PHONY: tidy +tidy: + @$(GO) mod tidy -# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) -ifneq (,$(shell go env GOBIN)) -GOBIN=$(shell go env GOPATH)/bin -else -GOBIN=$(shell go env GOBIN) -endif +## vendor: vendor go.mod +.PHONY: vendor +vendor: + @$(GO) mod vendor ## fmt: Run go fmt against code. +.PHONY: fmt fmt: - go fmt ./... + @$(GO) fmt ./... ## vet: Run go vet against code. +.PHONY: vet vet: - go vet ./... + @$(GO) vet ./... -## lint: Run go lint against code. +## lint: Check syntax and styling of go sources. +.PHONY: lint lint: - golangci-lint run -v ./... + @$(MAKE) go.lint -## style: code style: fmt,vet,lint +## style: code style -> fmt,vet,lint +.PHONY: style style: fmt vet lint -## build: Build binaries by default -build: clean - @echo "build sealer and seautil bin" - $(TOOLS_DIR) +## linux: Build the all with a build script +.PHONY: linux +linux: + @$(MAKE) go.linux-a -## linux: Build binaries for Linux -linux: clean - @echo "Building sealer and seautil binaries for Linux (amd64)" - GOOS=$(GOOS) GOARCH=$(GOARCH) $(TOOLS_DIR) $(GIT_TAG) +## linux.%: Build binaries for Linux (make linux.amd64 OR make linux.arm64) +linux.%: + @$(MAKE) go.linux.$* -## linux-amd64: Build binaries for Linux (amd64) -linux-amd64: clean - @echo "Building sealer and seautil binaries for Linux (amd64)" - GOOS=linux GOARCH=amd64 $(TOOLS_DIR) $(GIT_TAG) +## format: Gofmt (reformat) package sources (exclude vendor dir if existed). +.PHONY: format +format: + @$(MAKE) go.format -## linux-arm64: Build binaries for Linux (arm64) -linux-arm64: clean - @echo "Building sealer and seautil binaries for Linux (arm64)" - GOOS=linux GOARCH=arm64 $(TOOLS_DIR) $(GIT_TAG) +## test: Run unit test. +.PHONY: test +test: + @$(MAKE) go.test -## build-in-docker: sealer should be compiled in linux platform, otherwise there will be GraphDriver problem. -build-in-docker: - docker run --rm -v ${PWD}:/usr/src/sealer -w /usr/src/sealer registry.cn-qingdao.aliyuncs.com/sealer-io/sealer-build:v1 make linux +## cover: Run unit test and get test coverage. +.PHONY: cover +cover: + @$(MAKE) go.test.cover + +## updates: Check for updates to go.mod dependencies +.PHONY: updates + @$(MAKE) go.updates + +## imports: task to automatically handle import packages in Go files using goimports tool +.PHONY: imports +imports: + @$(MAKE) go.imports ## clean: Remove all files that are created by building. .PHONY: clean clean: - @echo "===========> Cleaning all build output" - @-rm -rf _output - -## install-addlicense: check license if not exist install addlicense tools -install-addlicense: -ifeq (, $(shell which addlicense)) - @{ \ - set -e ;\ - LICENSE_TMP_DIR=$$(mktemp -d) ;\ - cd $$LICENSE_TMP_DIR ;\ - go mod init tmp ;\ - go get -v github.com/google/addlicense ;\ - rm -rf $$LICENSE_TMP_DIR ;\ - } -ADDLICENSE_BIN=$(GOBIN)/addlicense -else -ADDLICENSE_BIN=$(shell which addlicense) -endif - -filelicense: SHELL:=/bin/bash -## filelicense: add license -filelicense: - for file in ${Dirs} ; do \ - if [[ $$file != '_output' && $$file != 'docs' && $$file != 'vendor' && $$file != 'logger' && $$file != 'applications' ]]; then \ - $(ADDLICENSE_BIN) -y $(shell date +"%Y") -c "Alibaba Group Holding Ltd." -f hack/LICENSE_TEMPLATE ./$$file ; \ - fi \ - done - - -## install-gosec: check license if not exist install addlicense tools -install-gosec: -ifeq (, $(shell which gosec)) - @{ \ - set -e ;\ - curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $(GOBIN) v2.2.0 ;\ - } -GOSEC_BIN=$(GOBIN)/gosec -else -GOSEC_BIN=$(shell which gosec) -endif - -gosec: install-gosec - $(GOSEC_BIN) ./... - - -install-deepcopy-gen: -ifeq (, $(shell which deepcopy-gen)) - @{ \ - set -e ;\ - LICENSE_TMP_DIR=$$(mktemp -d) ;\ - cd $$LICENSE_TMP_DIR ;\ - go mod init tmp ;\ - go get -v k8s.io/code-generator/cmd/deepcopy-gen ;\ - rm -rf $$LICENSE_TMP_DIR ;\ - } -DEEPCOPY_BIN=$(GOBIN)/deepcopy-gen -else -DEEPCOPY_BIN=$(shell which deepcopy-gen) -endif - -HEAD_FILE := hack/boilerplate.go.txt -INPUT_DIR := github.com/sealerio/sealer/types/api -deepcopy:install-deepcopy-gen - $(DEEPCOPY_BIN) \ - --input-dirs="$(INPUT_DIR)/v1" \ - -O zz_generated.deepcopy \ - --go-header-file "$(HEAD_FILE)" \ - --output-base "${GOPATH}/src" - $(DEEPCOPY_BIN) \ - --input-dirs="$(INPUT_DIR)/v2" \ - -O zz_generated.deepcopy \ - --go-header-file "$(HEAD_FILE)" \ - --output-base "${GOPATH}/src" - -## help: Display help information + @$(MAKE) go.clean + +## tools: Install dependent tools. +.PHONY: tools +tools: + @$(MAKE) tools.install + +## build-in-docker: sealer should be compiled in linux platform, otherwise there will be GraphDriver problem. +build-in-docker: + @docker run --rm -v ${PWD}:/usr/src/sealer -w /usr/src/sealer registry.cn-qingdao.aliyuncs.com/sealer-io/sealer-build:v1 make linux + +## gen: Generate all necessary files. +.PHONY: gen +gen: + @$(MAKE) gen.run + +## verify-copyright: Verify the license headers for all files. +.PHONY: verify-copyright +verify-copyright: + @$(MAKE) copyright.verify + +## add-copyright: Add copyright ensure source code files have license headers. +.PHONY: add-copyright +add-copyright: + @$(MAKE) copyright.add + +## help: Show this help info. +.PHONY: help help: Makefile - @echo "" - @echo "Usage:" "\n" - @echo " make [target]" "\n" - @echo "Targets:" "\n" "" - @awk -F ':|##' '/^[^\.%\t][^\t]*:.*##/{printf " \033[36m%-20s\033[0m %s\n", $$1, $$NF}' $(MAKEFILE_LIST) | sort - @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' + $(call makehelp) + +## all-help: Show all help details info. +.PHONY: help-all +help-all: go.help copyright.help tools.help image.help help + $(call makeallhelp) diff --git a/build/buildimage/differ.go b/build/buildimage/differ.go index 40b89e3c5eb..11c39236967 100644 --- a/build/buildimage/differ.go +++ b/build/buildimage/differ.go @@ -37,7 +37,7 @@ import ( osi "github.com/sealerio/sealer/utils/os" ) -// TODO: update the variable name +// TODO: update the variable name. var ( copyToManifests = "manifests" copyToChart = "charts" @@ -45,8 +45,10 @@ var ( copyToApplication = "application" ) -type parseContainerImageStringSliceFunc func(srcPath string) ([]string, error) -type parseContainerImageListFunc func(srcPath string) ([]*v12.ContainerImage, error) +type ( + parseContainerImageStringSliceFunc func(srcPath string) ([]string, error) + parseContainerImageListFunc func(srcPath string) ([]*v12.ContainerImage, error) +) var parseContainerImageStringSliceFuncMap = map[string]func(srcPath string) ([]string, error){ copyToManifests: parseYamlImages, @@ -274,7 +276,6 @@ func parseApplicationKubeImages(kubePath string) ([]string, error) { } ima, err := imageSearcher.ListImages(path) - if err != nil { return err } @@ -351,7 +352,6 @@ func parseYamlImages(srcPath string) ([]string, error) { } ima, err := imageSearcher.ListImages(path) - if err != nil { return err } diff --git a/build/kubefile/command/command.go b/build/kubefile/command/command.go index e14338dc60c..c9dc3dff3de 100644 --- a/build/kubefile/command/command.go +++ b/build/kubefile/command/command.go @@ -40,7 +40,7 @@ const ( // Deprecated Cmd = "cmd" - // the following commands are the intenal implementations for kube commands + // the following commands are the intenal implementations for kube commands. Add = "add" Arg = "arg" Copy = "copy" @@ -59,7 +59,7 @@ var ( LabelKubeCSIPrefix = fmt.Sprintf("%s-", LabelSupportedKubeCSIAlpha) ) -// SupportedCommands is list of all Kubefile commands +// SupportedCommands is list of all Kubefile commands. var SupportedCommands = map[string]struct{}{ Add: {}, Arg: {}, diff --git a/cmd/sealer/cmd/image/build.go b/cmd/sealer/cmd/image/build.go index dcbbea1720a..b4d6afed432 100644 --- a/cmd/sealer/cmd/image/build.go +++ b/cmd/sealer/cmd/image/build.go @@ -28,11 +28,11 @@ import ( "github.com/sealerio/sealer/pkg/imageengine" "github.com/sealerio/sealer/pkg/imageengine/buildah" "github.com/sealerio/sealer/pkg/rootfs" + "github.com/sealerio/sealer/pkg/version" v1 "github.com/sealerio/sealer/types/api/v1" osi "github.com/sealerio/sealer/utils/os" "github.com/sealerio/sealer/utils/strings" "github.com/sealerio/sealer/utils/yaml" - "github.com/sealerio/sealer/version" "github.com/containerd/containerd/platforms" "github.com/containers/buildah/define" diff --git a/cmd/sealer/cmd/root.go b/cmd/sealer/cmd/root.go index 4bc0389d2f9..d9c2233d6df 100644 --- a/cmd/sealer/cmd/root.go +++ b/cmd/sealer/cmd/root.go @@ -28,7 +28,7 @@ import ( "github.com/sealerio/sealer/cmd/sealer/cmd/image" "github.com/sealerio/sealer/common" "github.com/sealerio/sealer/pkg/logger" - "github.com/sealerio/sealer/version" + "github.com/sealerio/sealer/pkg/version" ) type rootOpts struct { diff --git a/cmd/sealer/cmd/version.go b/cmd/sealer/cmd/version.go index d22c8701657..0f9b9793ad7 100644 --- a/cmd/sealer/cmd/version.go +++ b/cmd/sealer/cmd/version.go @@ -19,31 +19,89 @@ import ( "fmt" "github.com/spf13/cobra" + "gopkg.in/yaml.v2" - "github.com/sealerio/sealer/version" + "github.com/sealerio/sealer/pkg/version" ) -var shortPrint bool +var ( + shortPrint bool + output string +) +var sealerErr error func NewVersionCmd() *cobra.Command { versionCmd := &cobra.Command{ Use: "version", - Short: "show sealer and related versions", + Short: "Print version info", Args: cobra.NoArgs, Example: `sealer version`, RunE: func(cmd *cobra.Command, args []string) error { - marshalled, err := json.Marshal(version.Get()) - if err != nil { - return err + // Validate validates the provided options. + if output != "" && output != "yaml" && output != "json" { + return fmt.Errorf("output format must be yaml or json") } if shortPrint { fmt.Println(version.Get().String()) - } else { - fmt.Println(string(marshalled)) + return nil } - return nil + return PrintInfo() }, } - versionCmd.Flags().BoolVar(&shortPrint, "short", false, "if true, print sealer's own version number.") + versionCmd.Flags().BoolVar(&shortPrint, "short", false, "If true, print just the version number.") + versionCmd.Flags().StringVarP(&output, "output", "o", "yaml", "choose `yaml` or `json` format to print version info") return versionCmd } + +func PrintInfo() error { + OutputInfo := &version.Output{} + OutputInfo.SealerVersion = version.Get() + + if err := PrintToStd(OutputInfo); err != nil { + return err + } + //TODO! + // missinfo := []string{} + // if OutputInfo.KubernetesVersion == nil { + // missinfo = append(missinfo, "kubernetes version") + // } + // if OutputInfo.CriRuntimeVersion == nil { + // missinfo = append(missinfo, "cri runtime version") + // } + // if OutputInfo.KubernetesVersion == nil || OutputInfo.CriRuntimeVersion == nil { + // fmt.Printf("WARNING: Failed to get %s.\nCheck kubernetes status or use command \"sealer run\" to launch kubernetes\n", strings.Join(missinfo, " and ")) + // } + // if OutputInfo.K0sVersion == nil { + // fmt.Println("WARNING: Failed to get k0s version.\nCheck k0s status or use command \"sealer run\" to launch k0s\n") + // } + // if OutputInfo.K3sVersion == nil { + // fmt.Println("WARNING: Failed to get k3s version.\nCheck k3s status or use command \"sealer run\" to launch k3s\n") + // } + return nil +} + +func PrintToStd(OutputInfo *version.Output) error { + var ( + marshalled []byte + err error + ) + switch output { + case "yaml": + marshalled, err = yaml.Marshal(&OutputInfo) + if err != nil { + return fmt.Errorf("fail to marshal yaml: %w", err) + } + fmt.Println(string(marshalled)) + case "json": + marshalled, err = json.Marshal(&OutputInfo) + if err != nil { + return fmt.Errorf("fail to marshal json: %w", err) + } + fmt.Println(string(marshalled)) + default: + // There is a bug in the program if we hit this case. + // However, we follow a policy of never panicking. + return fmt.Errorf("versionOptions were not validated: --output=%q should have been rejected", output) + } + return sealerErr +} diff --git a/cmd/seautil/cmd/version.go b/cmd/seautil/cmd/version.go index 68f3140d948..21a56becf2b 100644 --- a/cmd/seautil/cmd/version.go +++ b/cmd/seautil/cmd/version.go @@ -16,35 +16,90 @@ package cmd import ( "encoding/json" - "fmt" //nolint:imports - "os" + "fmt" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "gopkg.in/yaml.v2" - "github.com/sealerio/sealer/version" + "github.com/sealerio/sealer/pkg/version" ) -var shortPrint bool +var ( + shortPrint bool + output string +) func NewVersionCmd() *cobra.Command { versionCmd := &cobra.Command{ - Use: "version", - Short: "version", - Long: `sealer version`, - Run: func(cmd *cobra.Command, args []string) { - marshalled, err := json.Marshal(version.Get()) - if err != nil { - logrus.Error(err) - os.Exit(1) + Use: "version", + Short: "Print version info", + Args: cobra.NoArgs, + Example: `seautil version`, + RunE: func(cmd *cobra.Command, args []string) error { + //output default to be yaml + if output == "yaml" && output == "json" { + return fmt.Errorf("output format must be yaml or json") } if shortPrint { fmt.Println(version.Get().String()) - } else { - fmt.Println(string(marshalled)) + return nil } + return PrintInfo() }, } versionCmd.Flags().BoolVar(&shortPrint, "short", false, "If true, print just the version number.") + versionCmd.Flags().StringVarP(&output, "output", "o", "yaml", "choose `yaml` or `json` format to print version info") return versionCmd } + +func PrintInfo() error { + OutputInfo := &version.Output{} + OutputInfo.SealerVersion = version.Get() + + if err := PrintToStd(OutputInfo); err != nil { + return err + } + // missinfo := []string{} + // if OutputInfo.KubernetesVersion == nil { + // missinfo = append(missinfo, "kubernetes version") + // } + // if OutputInfo.CriRuntimeVersion == nil { + // missinfo = append(missinfo, "cri runtime version") + // } + // if OutputInfo.KubernetesVersion == nil || OutputInfo.CriRuntimeVersion == nil { + // fmt.Printf("WARNING: Failed to get %s.\nCheck kubernetes status or use command \"sealer run\" to launch kubernetes\n", strings.Join(missinfo, " and ")) + // } + // if OutputInfo.K0sVersion == nil { + // fmt.Println("WARNING: Failed to get k0s version.\nCheck k0s status or use command \"sealer run\" to launch k0s\n") + // } + // if OutputInfo.K3sVersion == nil { + // fmt.Println("WARNING: Failed to get k3s version.\nCheck k3s status or use command \"sealer run\" to launch k3s\n") + // } + return nil +} + +func PrintToStd(OutputInfo *version.Output) error { + var ( + marshalled []byte + err error + ) + switch output { + case "yaml": + marshalled, err = yaml.Marshal(&OutputInfo) + if err != nil { + return fmt.Errorf("fail to marshal yaml: %w", err) + } + fmt.Println(string(marshalled)) + case "json": + marshalled, err = json.Marshal(&OutputInfo) + if err != nil { + return fmt.Errorf("fail to marshal json: %w", err) + } + fmt.Println(string(marshalled)) + default: + // There is a bug in the program if we hit this case. + // However, we follow a policy of never panicking. + return fmt.Errorf("versionOptions were not validated: --output=%q should have been rejected", output) + } + return nil +} diff --git a/go.mod b/go.mod index e71805ffe05..28ca087b0ac 100644 --- a/go.mod +++ b/go.mod @@ -89,6 +89,7 @@ require ( github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/fsouza/go-dockerclient v1.8.1 // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -108,6 +109,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/mux v1.8.0 // indirect + github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -127,6 +129,8 @@ require ( github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/manifoldco/promptui v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect diff --git a/go.sum b/go.sum index 89f651a5a87..6f6e707c28d 100644 --- a/go.sum +++ b/go.sum @@ -614,6 +614,7 @@ github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= @@ -1015,6 +1016,7 @@ github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/g github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -1346,6 +1348,7 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= @@ -1360,6 +1363,7 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= diff --git a/hack/build.sh b/hack/build.sh deleted file mode 100755 index 76c0c8958fe..00000000000 --- a/hack/build.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/bin/bash -# Copyright © 2021 Alibaba Group Holding Ltd. -# -# 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. - -# ----------------------------------------------------------------------------- -# Build management helpers. These functions help to set, save and load the -# following variables: -# -# GIT_TAG - The version for sealer. -# MULTI_PLATFORM_BUILD - Need build all platform.(linux and darwin) - - -export GO111MODULE=on -set -x -SEALER_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" -export THIS_PLATFORM_BIN="${SEALER_ROOT}/_output/bin" -export THIS_PLATFORM_ASSETS="${SEALER_ROOT}/_output/assets" - -# fix containers dependency issue -# https://github.com/containers/image/pull/271/files -# for btrfs, we just use overlay at present, so there is no need to include btrfs, otherwise we need fix some lib problems -GO_BUILD_FLAGS="containers_image_openpgp exclude_graphdriver_devicemapper exclude_graphdriver_btrfs" - -debug() { - timestamp=$(date +"[%m%d %H:%M:%S]") - echo "[debug] ${timestamp} ${1-}" >&2 - shift - for message; do - echo " ${message}" >&2 - done -} - -get_version_vars() { - GIT_VERSION=unknown - if [[ $GIT_TAG ]]; then - GIT_VERSION=$GIT_TAG - fi - GIT_COMMIT=`git rev-parse --short HEAD || true` - if [[ -z $GIT_COMMIT ]]; then - GIT_COMMIT="0.0.0" - fi - - debug "version: $GIT_VERSION" - debug "commit id: $GIT_COMMIT" -} - -ldflags() { - local -a ldflags - function add_ldflag() { - local key=${1} - local val=${2} - # If you update these, also update the list component-base/version/def.bzl. - ldflags+=( - "-X '${SEALER_GO_PACKAGE}/version.${key}=${val}'" - ) - } - add_ldflag "buildDate" "$(date "+%F %T")" - if [[ -n ${GIT_COMMIT-} ]]; then - add_ldflag "gitCommit" "${GIT_COMMIT}" - fi - - if [[ -n ${GIT_VERSION-} ]]; then - add_ldflag "gitVersion" "${GIT_VERSION}" - fi - - # The -ldflags parameter takes a single string, so join the output. - echo "${ldflags[*]-}" -} - -readonly SEALER_GO_PACKAGE=github.com/sealerio/sealer -# The server platform we are building on. -readonly SEALER_SUPPORTED_PLATFORMS=( - linux/amd64 - linux/arm64 -) - -check() { - timestamp=$(date +"[%m%d %H:%M:%S]") - ret=$1 - if [[ $ret -eq 0 ]];then - echo "[info] ${timestamp} ${2-} up to date." - else - echo "[err] ${timestamp} ${2-} is out of date. Please run $0" - exit 1 - fi -} - -build_binaries() { - get_version_vars - goldflags="${GOLDFLAGS=-s -w} $(ldflags)" - osarch=${1-}_${2-} - mkdir -p $THIS_PLATFORM_ASSETS - tarFile="${GIT_VERSION}-${1-}-${2-}.tar.gz" - - debug "!!! build $osarch sealer" - GOOS=${1-} GOARCH=${2-} go build -tags "${GO_BUILD_FLAGS}" -o $THIS_PLATFORM_BIN/sealer/$osarch/sealer -mod vendor -ldflags "$goldflags" $SEALER_ROOT/cmd/sealer/main.go - check $? "build $osarch sealer" - debug "output bin: $THIS_PLATFORM_BIN/sealer/$osarch/sealer" - cd ${SEALER_ROOT}/_output/bin/sealer/$osarch/ - tar czf sealer-$tarFile sealer - sha256sum sealer-$tarFile > sealer-$tarFile.sha256sum - mv *.tar.gz* $THIS_PLATFORM_ASSETS/ - debug "output tar.gz: $THIS_PLATFORM_ASSETS/sealer-$tarFile" - debug "output sha256sum: $THIS_PLATFORM_ASSETS/sealer-$tarFile.sha256sum" - - debug "!!! build $osarch seautil" - GOOS=${1-} GOARCH=${2-} go build -o $THIS_PLATFORM_BIN/seautil/$osarch/seautil -mod vendor -ldflags "$goldflags" $SEALER_ROOT/cmd/seautil/main.go - check $? "build $osarch seautil" - debug "output bin: $THIS_PLATFORM_BIN/seautil/$osarch/seautil" - cd ${SEALER_ROOT}/_output/bin/seautil/$osarch/ - tar czf seautil-$tarFile seautil - sha256sum seautil-$tarFile > seautil-$tarFile.sha256sum - mv *.tar.gz* $THIS_PLATFORM_ASSETS/ - debug "output tar.gz: $THIS_PLATFORM_ASSETS/seautil-$tarFile" - debug "output sha256sum: $THIS_PLATFORM_ASSETS/seautil-$tarFile.sha256sum" - -} - -debug "root dir: $SEALER_ROOT" -debug "build dir: $THIS_PLATFORM_BIN" - -#Multi platform -if [[ $MULTI_PLATFORM_BUILD ]]; then - for platform in "${SEALER_SUPPORTED_PLATFORMS[@]}"; do - OS=${platform%/*} - ARCH=${platform##*/} - build_binaries $OS $ARCH - done; -else - build_binaries `go env GOOS` `go env GOARCH` -fi - diff --git a/pkg/checker/node_checker.go b/pkg/checker/node_checker.go index 992e8571065..2b9648baba2 100644 --- a/pkg/checker/node_checker.go +++ b/pkg/checker/node_checker.go @@ -88,7 +88,7 @@ func (n *NodeChecker) Check(cluster *v2.Cluster, phase string) error { } func (n *NodeChecker) Output(nodeCLusterStatus NodeClusterStatus) error { - //t1, err := template.ParseFiles("templates/node_checker.tpl") + // t1, err := template.ParseFiles("templates/node_checker.tpl") t := template.New("node_checker") t, err := t.Parse( `Cluster Node Status diff --git a/pkg/env/test/template/kubeadm.yaml b/pkg/env/test/template/kubeadm.yaml index 6c0ecad5ea3..02ec2c0e3c7 100755 --- a/pkg/env/test/template/kubeadm.yaml +++ b/pkg/env/test/template/kubeadm.yaml @@ -1,17 +1,3 @@ -# Copyright © 2023 Alibaba Group Holding Ltd. -# -# 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. - apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.19.8 diff --git a/version/base.go b/pkg/version/base.go similarity index 94% rename from version/base.go rename to pkg/version/base.go index 5ce150a191e..4923ab07d6c 100644 --- a/version/base.go +++ b/pkg/version/base.go @@ -53,8 +53,12 @@ var ( // NOTE: The $Format strings are replaced during 'git archive' thanks to the // companion .gitattributes file containing 'export-subst' in this same // directory. See also https://git-scm.com/docs/gitattributes - gitVersion = "unknown" + // GitVersion is semantic version. + gitVersion = "v0.0.0-main+$Format:%h$" gitCommit = "" // sha1 from git, output of $(git rev-parse HEAD) buildDate = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') + + // GitTreeState state of git tree, either "clean" or "dirty". + gitTreeState = "" ) diff --git a/pkg/version/types.go b/pkg/version/types.go new file mode 100644 index 00000000000..f8985f24bc9 --- /dev/null +++ b/pkg/version/types.go @@ -0,0 +1,90 @@ +// Copyright © 2021 Alibaba Group Holding Ltd. +// +// 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 version + +import ( + "fmt" +) + +// Info contains versioning information. +// TODO: Add []string of api versions supported? It's still unclear +// how we'll want to distribute that information. +type Info struct { + Major string `json:"major,omitempty"` + Minor string `json:"minor,omitempty"` + GitVersion string `json:"gitVersion"` + GitCommit string `json:"gitCommit,omitempty"` + GitTreeState string `json:"gitTreeState"` + BuildDate string `json:"buildDate"` + GoVersion string `json:"goVersion"` + Compiler string `json:"compiler"` + Platform string `json:"platform"` +} + +type Output struct { + SealerVersion Info `json:"sealerVersion,omitempty" yaml:"sealerVersion,omitempty"` + CriRuntimeVersion *CriRuntimeVersion `json:"criVersionInfo,omitempty" yaml:"criVersionInfo,omitempty"` + KubernetesVersion *KubernetesVersion `json:"kubernetesVersionInfo,omitempty" yaml:"kubernetesVersionInfo,omitempty"` + K0sVersion *k0sVersion `json:"k0sVersionInfo,omitempty" yaml:"k0sVersionInfo,omitempty"` + K3sVersion *k3sVersion `json:"k3sVersionInfo,omitempty" yaml:"k3sVersionInfo,omitempty"` +} + +type CriRuntimeVersion struct { + // Version of the kubelet runtime API. + Version string `json:"Version,omitempty" yaml:"Version,omitempty"` + // Name of the container runtime. + RuntimeName string `json:"RuntimeName,omitempty" yaml:"RuntimeName,omitempty"` + // Version of the container runtime. The string must be + // semver-compatible. + RuntimeVersion string `json:"RuntimeVersion,omitempty" yaml:"RuntimeVersion,omitempty"` + // API version of the container runtime. The string must be + // semver-compatible. + RuntimeAPIVersion string `json:"RuntimeApiVersion,omitempty" yaml:"RuntimeApiVersion,omitempty"` +} + +type KubernetesVersion struct { + ClientVersion *KubectlInfo `json:"clientVersion,omitempty" yaml:"clientVersion,omitempty"` + KustomizeVersion string `json:"kustomizeVersion,omitempty" yaml:"kustomizeVersion,omitempty"` + ServerVersion *KubectlInfo `json:"serverVersion,omitempty" yaml:"serverVersion,omitempty"` +} + +type k0sVersion struct { + K0sVersion string `json:"k9sVersion,omitempty" yaml:"k0sVersion,omitempty"` +} + +type k3sVersion struct { + K3sVersion string `json:"k3sVersion,omitempty" yaml:"k3sVersion,omitempty"` +} + +type KubectlInfo struct { + Major string `json:"major" yaml:"major"` + Minor string `json:"minor" yaml:"minor"` + GitVersion string `json:"gitVersion" yaml:"gitVersion"` + GitCommit string `json:"gitCommit" yaml:"gitCommit"` + GitTreeState string `json:"gitTreeState" yaml:"gitTreeState"` + BuildDate string `json:"buildDate" yaml:"buildDate"` + GoVersion string `json:"goVersion" yaml:"goVersion"` + Compiler string `json:"compiler" yaml:"compiler"` + Platform string `json:"platform" yaml:"platform"` +} + +// String returns info as a human-friendly version string. +func (info Info) String() string { + if s, err := info.Text(); err == nil { + return string(s) + } + + return fmt.Sprintf("%s-%s", info.GitVersion, info.GitCommit) +} diff --git a/version/version.go b/pkg/version/version.go similarity index 50% rename from version/version.go rename to pkg/version/version.go index 0bfafb5fd37..4f99cad3a5c 100644 --- a/version/version.go +++ b/pkg/version/version.go @@ -15,8 +15,11 @@ package version import ( + "encoding/json" "fmt" "runtime" + + "github.com/gosuri/uitable" ) // Get returns the overall codebase version. It's for detecting @@ -25,18 +28,44 @@ func Get() Info { // These variables typically come from -ldflags settings and in // their absence fallback to the settings in ./base.go return Info{ - Major: gitMajor, - Minor: gitMinor, - GitVersion: gitVersion, - GitCommit: gitCommit, - BuildDate: buildDate, - GoVersion: runtime.Version(), - Compiler: runtime.Compiler, - Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), + Major: gitMajor, + Minor: gitMinor, + GitVersion: gitVersion, + GitCommit: gitCommit, + GitTreeState: gitTreeState, + BuildDate: buildDate, + GoVersion: runtime.Version(), + Compiler: runtime.Compiler, + Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), } } + // GetSingleVersion returns single version of sealer func GetSingleVersion() string { return gitVersion } + +func (info Info) ToJSON() string { + s, _ := json.Marshal(info) + + return string(s) +} + +// Text encodes the version information into UTF-8-encoded text and +// returns the result. +func (info Info) Text() ([]byte, error) { + table := uitable.New() + table.RightAlign(0) + table.MaxColWidth = 80 + table.Separator = " " + table.AddRow("gitVersion:", info.GitVersion) + table.AddRow("gitCommit:", info.GitCommit) + table.AddRow("gitTreeState:", info.GitTreeState) + table.AddRow("buildDate:", info.BuildDate) + table.AddRow("goVersion:", info.GoVersion) + table.AddRow("compiler:", info.Compiler) + table.AddRow("platform:", info.Platform) + + return table.Bytes(), nil +} diff --git a/hack/LICENSE_TEMPLATE b/scripts/LICENSE_TEMPLATE similarity index 100% rename from hack/LICENSE_TEMPLATE rename to scripts/LICENSE_TEMPLATE diff --git a/hack/boilerplate.go.txt b/scripts/boilerplate.go.txt similarity index 100% rename from hack/boilerplate.go.txt rename to scripts/boilerplate.go.txt diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 00000000000..5e7303e8c81 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,234 @@ +#!/usr/bin/env bash + +# Copyright © 2022 Alibaba Group Holding Ltd. +# +# 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. + +# ============================================================================== + +## Build script + +# Build helper functions. These functions help to set up, save, and load the following variables: +# +# GIT_TAG - the version number of sealer +# MULTI_PLATFORM_BUILD - whether to build for all platforms (linux and darwin) + +# Set GO111MODULE=on to enable Go Modules when using go mod to manage dependencies +export GO111MODULE=on +# Turn on command tracing so that each command is output when the script is run +set -x +# Get the absolute path of the current script and set the variable SEALER_ROOT to this value +SEALER_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +# Set the variables THIS_PLATFORM_BIN and THIS_PLATFORM_ASSETS for use when building binaries and assets (such as tar.gz files) +export THIS_PLATFORM_BIN="${SEALER_ROOT}/_output/bin" +export THIS_PLATFORM_ASSETS="${SEALER_ROOT}/_output/assets" + +# Fixed container dependency issues +# Link -> https://github.com/containers/image/pull/271/files +# For btrfs, we are currently only using overlays, so we don't need to include btrfs, otherwise we need to fix some lib issues +GO_BUILD_FLAGS="containers_image_openpgp exclude_graphdriver_devicemapper exclude_graphdriver_btrfs" + +# Output debug information +debug() { + timestamp=$(date +"[%m%d %H:%M:%S]") + echo "[debug] ${timestamp} ${1-}" >&2 + shift + for message; do + echo " ${message}" >&2 + done +} + +# Get version information +get_version_vars() { + GIT_VERSION=unknown + if [[ $GIT_TAG ]]; then + GIT_VERSION=$GIT_TAG + fi + GIT_COMMIT=`git rev-parse --short HEAD || true` + if [[ -z $GIT_COMMIT ]]; then + GIT_COMMIT="0.0.0" + fi + + debug "version: $GIT_VERSION" + debug "commit id: $GIT_COMMIT" +} + +# Parameters used when building the program +# Used to pass parameters such as compilation time, version number, commit ID, etc. +ldflags() { + local -a ldflags + function add_ldflag() { + local key=${1} + local val=${2} + # If you update these, also update the list component-base/version/def.bzl. + ldflags+=( + "-X '${SEALER_GO_PACKAGE}/pkg/version.${key}=${val}'" + ) + } + add_ldflag "buildDate" "$(date "+%FT %T %z")" + if [[ -n ${GIT_COMMIT-} ]]; then + add_ldflag "gitCommit" "${GIT_COMMIT}" + fi + + if [[ -n ${GIT_VERSION-} ]]; then + add_ldflag "gitVersion" "${GIT_VERSION}" + fi + + # The -ldflags parameter takes a single string, so join the output. + echo "${ldflags[*]-}" +} + +# Package path for sealer source code +readonly SEALER_GO_PACKAGE=github.com/sealerio/sealer +# Platforms supported when building the program +readonly SEALER_SUPPORTED_PLATFORMS=( + linux/amd64 + linux/arm64 +) + +# Check if the program needs to be built +check() { + timestamp=$(date +"[%m%d %H:%M:%S]") + ret=$1 + if [[ $ret -eq 0 ]]; then + echo "[info] ${timestamp} ${2-} up to date." + else + echo "[err] ${timestamp} ${2-} is out of date. Please run $0" + exit 1 + fi +} + +# Function for building the program +build_binaries() { + get_version_vars + goldflags="${GOLDFLAGS=-s -w} $(ldflags)" + osarch=${1-}_${2-} + mkdir -p $THIS_PLATFORM_ASSETS + tarFile="${GIT_VERSION}-${1-}-${2-}.tar.gz" + + debug "!!! build $osarch sealer" + GOOS=${1-} GOARCH=${2-} go build -tags "${GO_BUILD_FLAGS}" -o $THIS_PLATFORM_BIN/sealer/$osarch/sealer -mod vendor -ldflags "$goldflags" $SEALER_ROOT/cmd/sealer/main.go + check $? "build $osarch sealer" + debug "output bin: $THIS_PLATFORM_BIN/sealer/$osarch/sealer" + cd ${SEALER_ROOT}/_output/bin/sealer/$osarch/ + tar czf sealer-$tarFile sealer + sha256sum sealer-$tarFile > sealer-$tarFile.sha256sum + mv *.tar.gz* $THIS_PLATFORM_ASSETS/ + debug "output tar.gz: $THIS_PLATFORM_ASSETS/sealer-$tarFile" + debug "output sha256sum: $THIS_PLATFORM_ASSETS/sealer-$tarFile.sha256sum" + + debug "!!! build $osarch seautil" + GOOS=${1-} GOARCH=${2-} go build -o $THIS_PLATFORM_BIN/seautil/$osarch/seautil -mod vendor -ldflags "$goldflags" $SEALER_ROOT/cmd/seautil/main.go + check $? "build $osarch seautil" + debug "output bin: $THIS_PLATFORM_BIN/seautil/$osarch/seautil" + cd ${SEALER_ROOT}/_output/bin/seautil/$osarch/ + tar czf seautil-$tarFile seautil + sha256sum seautil-$tarFile > seautil-$tarFile.sha256sum + mv *.tar.gz* $THIS_PLATFORM_ASSETS/ + debug "output tar.gz: $THIS_PLATFORM_ASSETS/seautil-$tarFile" + debug "output sha256sum: $THIS_PLATFORM_ASSETS/seautil-$tarFile.sha256sum" + debug "" +} + +# Display help information +show_help() { +cat << EOF +Usage: $0 [-h] [-p PLATFORMS] [-a] [-b BINARIES] + +Build Sealer binaries for one or more platforms. + DOTO: I recommend using a Makefile for a more immersive experience + + -h, --help display this help and exit + + -p, --platform build binaries for the specified platform(s), e.g. linux/amd64 or linux/arm64. + Multiple platforms should be separated by comma, e.g. linux/amd64,linux/arm64. + + -a, --all build binaries for all supported platforms + + -b, --binary build the specified binary/binaries, e.g. sealer or seautil. + Multiple binaries should be separated by comma, e.g. sealer,seautil. + (note: currently only supported in Makefile) + +EOF +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + show_help + exit 0 + ;; + -p|--platform) + shift + PLATFORMS=$1 + ;; + -a|--all) + ALL_PLATFORMS=true + ;; + -b|--binary) + shift + BINARIES=$1 + ;; + *) + echo "Unknown option: $1" + show_help + exit 1 + ;; + esac + shift +done + +debug "root dir: $SEALER_ROOT" +debug "build dir: $THIS_PLATFORM_BIN" + +# Build binaries for the specified platforms +if [[ -n "$PLATFORMS" ]]; then + IFS=',' read -ra PLATFORM_LIST <<< "$PLATFORMS" + for platform in "${PLATFORM_LIST[@]}"; do + OS=${platform%/*} + ARCH=${platform##*/} + build_binaries "$OS" "$ARCH" + done +# Build binaries for all supported platforms +elif [[ "$ALL_PLATFORMS" = true ]]; then + for platform in "${SEALER_SUPPORTED_PLATFORMS[@]}"; do + OS=${platform%/*} + ARCH=${platform##*/} + build_binaries "$OS" "$ARCH" + done +# Build the specified binaries +elif [[ -n "$BINARIES" ]]; then + IFS=',' read -ra BINARY_LIST <<< "$BINARIES" + for binary in "${BINARY_LIST[@]}"; do + case "$binary" in + sealer) + build_binaries `go env GOOS` `go env GOARCH` + ;; + seautil) + osarch=`go env GOOS`_`go env GOARCH` + GOOS=`go env GOOS` GOARCH=`go env GOARCH` go build -o $THIS_PLATFORM_BIN/seautil/$osarch/seautil -mod vendor -ldflags "$(ldflags)" $SEALER_ROOT/cmd/seautil/main.go + check $? "build seautil" + debug "output bin: $THIS_PLATFORM_BIN/seautil/$osarch/seautil" + ;; + *) + echo "Unknown binary: $binary" + show_help + exit 1 + ;; + esac + done +# Build all binaries for the current platform by default +else + build_binaries `go env GOOS` `go env GOARCH` +fi \ No newline at end of file diff --git a/scripts/coverage.awk b/scripts/coverage.awk new file mode 100644 index 00000000000..49389054e2d --- /dev/null +++ b/scripts/coverage.awk @@ -0,0 +1,13 @@ +#!/usr/bin/env awk + +{ + print $0 + if (match($0, /^total:/)) { + sub(/%/, "", $NF); + printf("test coverage is %s%(quality gate is %s%)\n", $NF, target) + if (strtonum($NF) < target) { + printf("test coverage does not meet expectations: %d%, please add test cases!\n", target) + exit 1; + } + } +} diff --git a/scripts/make-rules/common.mk b/scripts/make-rules/common.mk new file mode 100644 index 00000000000..2e588b8d6d9 --- /dev/null +++ b/scripts/make-rules/common.mk @@ -0,0 +1,170 @@ +# Copyright © 2022 Alibaba Group Holding Ltd. +# +# 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. + +# ============================================================================== +# Makefile helper functions for common tasks +# + +SHELL := /bin/bash +GO:=go +DIRS=$(shell ls) +DEBUG ?= 0 +GIT_TAG := $(shell git describe --exact-match --tags --abbrev=0 2> /dev/null || echo untagged) +GIT_COMMIT ?= $(shell git rev-parse --short HEAD || echo "0.0.0") +BUILD_DATE ?=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') # Blank error: date '+%FT %T %z':"buildDate":"2023-03-31T 20:05:43 +0800" + +# include the common makefile +COMMON_SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) + +SRC = $(shell find . -type f -name '*.go' -not -path "./vendor/*") + +# ROOT_DIR: root directory of the code base +ifeq ($(origin ROOT_DIR),undefined) +ROOT_DIR := $(abspath $(shell cd $(COMMON_SELF_DIR)/../.. && pwd -P)) +endif + +# OUTPUT_DIR: The directory where the build output is stored. +ifeq ($(origin OUTPUT_DIR),undefined) +OUTPUT_DIR := $(ROOT_DIR)/_output +$(shell mkdir -p $(OUTPUT_DIR)) +endif + +# BIN_DIR: Directory where executable files are stored. +ifeq ($(origin BIN_DIR),undefined) +BIN_DIR := $(OUTPUT_DIR)/bin +$(shell mkdir -p $(BIN_DIR)) +endif + +# TOOLS_DIR: The directory where tools are stored for build and testing. +ifeq ($(origin TOOLS_DIR),undefined) +TOOLS_DIR := $(ROOT_DIR)/tools +$(shell mkdir -p $(TOOLS_DIR)) +endif + +# TMP_DIR: directory where temporary files are stored. +ifeq ($(origin TMP_DIR),undefined) +TMP_DIR := $(ROOT_DIR)/tmp +$(shell mkdir -p $(TMP_DIR)) +endif + +ifeq ($(origin VERSION), undefined) +VERSION := $(shell git describe --abbrev=0 --dirty --always --tags | sed 's/-/./g') +endif + +# Check if the tree is dirty. default to dirty(maybe u should commit?) +GIT_TREE_STATE:="dirty" +ifeq (, $(shell git status --porcelain 2>/dev/null)) + GIT_TREE_STATE="clean" +endif +GIT_COMMIT:=$(shell git rev-parse HEAD) + +# Minimum test coverage +ifeq ($(origin COVERAGE),undefined) +COVERAGE := 60 +endif + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# platforms: The OS must be linux when building docker images +PLATFORMS ?= linux_amd64 linux_arm64 + +# only support linux +GOOS=linux + +# set a specific PLATFORM, defaults to the host platform +ifeq ($(origin PLATFORM), undefined) + ifeq ($(origin GOARCH), undefined) + GOARCH := $(shell go env GOARCH) + endif + ifeq ($(origin GOARCH), undefined) + GOARCH := $(shell go env GOARCH) + endif + PLATFORM := $(GOOS)_$(GOARCH) + # Use linux as the default OS when building images + IMAGE_PLAT := linux_$(GOARCH) +else + # such as: PLATFORM = linux_amd64 + GOOS := $(word 1, $(subst _, ,$(PLATFORM))) + GOARCH := $(word 2, $(subst _, ,$(PLATFORM))) + IMAGE_PLAT := $(PLATFORM) +endif + +# Linux command settings +# TODO: Whether you need to join utils? +FIND := find . ! -path './utils/*' ! -path './vendor/*' +XARGS := xargs -r + +# Linux command settings-CODE DIRS Copyright +# $$file != '_output' && $$file != 'docs' && $$file != 'vendor' && $$file != 'logger' && $$file != 'applications' +CODE_DIRS := $(ROOT_DIR)/pkg $(ROOT_DIR)/cmd $(ROOT_DIR)/test $(ROOT_DIR)/build $(ROOT_DIR)/scripts $(ROOT_DIR)/utils $(ROOT_DIR)/common +FINDS := find $(CODE_DIRS) + +# Makefile settings: Select different behaviors by determining whether V option is set +ifeq ($(origin V), undefined) # ifndef V +MAKEFLAGS += --no-print-directory +endif + +# COMMA: Concatenate multiple strings to form a list of strings +COMMA := , +# SPACE: Used to separate strings +SPACE := +# SPACE: Replace multiple consecutive Spaces with a single space +SPACE += + +# ============================================================================== +# Makefile helper functions for common tasks + +# Help information for the makefile package +define makehelp + @printf "\n\033[1mUsage: make ...\033[0m\n\n\\033[1mTargets:\\033[0m\n\n" + @sed -n 's/^##//p' $< | awk -F':' '{printf "\033[36m%-28s\033[0m %s\n", $$1, $$2}' | sed -e 's/^/ /' + @printf "\n\033[1m$$USAGE_OPTIONS\033[0m\n" +endef + +# Here are some examples of builds +define MAKEFILE_EXAMPLE +# make build BINS=sealer Only a single sealer binary is built. +# make -j $(nproc) all Run tidy gen add-copyright format lint cover build concurrently. +# make gen Generate all necessary files. +# make linux.arm64 sealer is compiled on arm64 platform. +# make verify-copyright Verify the license headers for all files. +# make install-deepcopy-gen Install deepcopy-gen tools if the license is missing. +# make build BINS=sealer V=1 DEBUG=1 Build debug binaries for only sealer. +# make build.multiarch PLATFORMS="linux_arm64 linux_amd64" V=1 Build binaries for both platforms. +endef +export MAKEFILE_EXAMPLE + +# Define all help functions @printf "\n\033[1mCurrent sealer version information: $(shell sealer version):\033[0m\n\n" +define makeallhelp + @printf "\n\033[1mMake example:\033[0m\n\n" + $(call MAKEFILE_EXAMPLE) + @printf "\n\033[1mAriables:\033[0m\n\n" + @echo " DEBUG: $(DEBUG)" + @echo " BINS: $(BINS)" + @echo " PLATFORMS: $(PLATFORMS)" + @echo " V: $(V)" +endef + +# Help information for other makefile packages +CUT_OFF?="---------------------------------------------------------------------------------" +HELP_NAME:=$(shell basename $(MAKEFILE_LIST)) +define smallhelp + @sed -n 's/^##//p' $< | awk -F':' '{printf "\033[36m%-35s\033[0m %s\n", $$1, $$2}' | sed -e 's/^/ /' + @echo $(CUT_OFF) +endef \ No newline at end of file diff --git a/scripts/make-rules/copyright.mk b/scripts/make-rules/copyright.mk new file mode 100644 index 00000000000..8002c92a0a5 --- /dev/null +++ b/scripts/make-rules/copyright.mk @@ -0,0 +1,59 @@ +# Copyright © 2022 Alibaba Group Holding Ltd. +# +# 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. +# +# ============================================================================== +# wget https://github.com/google/addlicense/releases/download/v1.0.0/addlicense_1.0.0_Linux_x86_64.tar.gz +# Makefile helper functions for copyright +# + +LICENSE_TEMPLATE ?= $(ROOT_DIR)/scripts/LICENSE_TEMPLATE + +# TODO: GOBIN -> TOOLS_DIR +# Questions about go mod instead of go path: https://github.com/kubernetes/kubernetes/issues/117181 +## copyright.verify: Validate boilerplate headers for assign files +.PHONY: copyright.verify +copyright.verify: tools.verify.addlicense + @echo "===========> Validate boilerplate headers for assign files starting in the $(ROOT_DIR) directory" + @$(GOBIN)/addlicense -v -check -ignore **/test/** -f $(LICENSE_TEMPLATE) $(CODE_DIRS) + @echo "===========> End of boilerplate headers check..." + +## copyright.add: Add the boilerplate headers for all files +.PHONY: copyright.add +copyright.add: tools.verify.addlicense + @echo "===========> Adding $(LICENSE_TEMPLATE) the boilerplate headers for all files" + @$(GOBIN)/addlicense -y $(shell date +"%Y") -v -c "Alibaba Group Holding Ltd." -f $(LICENSE_TEMPLATE) $(CODE_DIRS) + @echo "===========> End the copyright is added..." + +# Addlicense Flags: +# -c string +# copyright holder (default "Google LLC") +# -check +# check only mode: verify presence of license headers and exit with non-zero code if missing +# -f string +# license file +# -ignore value +# file patterns to ignore, for example: -ignore **/*.go -ignore vendor/** +# -l string +# license type: apache, bsd, mit, mpl (default "apache") +# -s Include SPDX identifier in license header. Set -s=only to only include SPDX identifier. +# -skip value +# [deprecated: see -ignore] file extensions to skip, for example: -skip rb -skip go +# -v verbose mode: print the name of the files that are modified or were skipped +# -y string +# copyright year(s) (default "2023") + +## copyright.help: Show copyright help +.PHONY: copyright.help +copyright.help: scripts/make-rules/copyright.mk + $(call smallhelp) \ No newline at end of file diff --git a/scripts/make-rules/dependencies.mk b/scripts/make-rules/dependencies.mk new file mode 100644 index 00000000000..de879589638 --- /dev/null +++ b/scripts/make-rules/dependencies.mk @@ -0,0 +1,36 @@ +# Copyright © 2022 Alibaba Group Holding Ltd. +# +# 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. + +# ============================================================================== +# Makefile helper functions for dependencies +# + +.PHONY: dependencies.run +dependencies.run: dependencies.packages dependencies.tools + +.PHONY: dependencies.packages +dependencies.packages: + @$(GO) mod tidy + +.PHONY: dependencies.tools +dependencies.tools: dependencies.tools.blocker dependencies.tools.critical + +.PHONY: dependencies.tools.blocker +dependencies.tools.blocker: go.build.verify $(addprefix tools.verify., $(BLOCKER_TOOLS)) + +.PHONY: dependencies.tools.critical +dependencies.tools.critical: $(addprefix tools.verify., $(CRITICAL_TOOLS)) + +.PHONY: dependencies.tools.trivial +dependencies.tools.trivial: $(addprefix tools.verify., $(TRIVIAL_TOOLS)) diff --git a/scripts/make-rules/gen.mk b/scripts/make-rules/gen.mk new file mode 100644 index 00000000000..7519bb23485 --- /dev/null +++ b/scripts/make-rules/gen.mk @@ -0,0 +1,103 @@ +# Copyright © 2022 Alibaba Group Holding Ltd. +# +# 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. + +# ============================================================================== +# Makefile helper functions for generate necessary files and docs +# https://cloud.redhat.com/blog/kubernetes-deep-dive-code-generation-customresources +# ! The stock of code generated by `make gen` should be idempotent +# + +## install-deepcopy-gen: check license if not exist install deepcopy-gen tools +# install-deepcopy-gen: +# ifeq (, $(shell which deepcopy-gen)) +# { \ +# set -e ;\ +# LICENSE_TMP_DIR=$$(mktemp -d) ;\ +# cd $$LICENSE_TMP_DIR ;\ +# go mod init tmp ;\ +# go get -v k8s.io/code-generator/cmd/deepcopy-gen ;\ +# rm -rf $$LICENSE_TMP_DIR ;\ +# } +# DEEPCOPY_BIN=$(GOBIN)/deepcopy-gen +# else +# DEEPCOPY_BIN=$(shell which deepcopy-gen) +# endif + +# BOILERPLATE := scripts/boilerplate.go.txt +# INPUT_DIR := github.com/sealerio/sealer/types/api + +## deepcopy: generate deepcopy code +# deepcopy: install-deepcopy-gen +# $(DEEPCOPY_BIN) \ +# --input-dirs="$(INPUT_DIR)/v1" \ +# -O zz_generated.deepcopy \ +# --go-header-file "$(BOILERPLATE)" \ +# --output-base "${GOPATH}/src" +# $(DEEPCOPY_BIN) \ +# --input-dirs="$(INPUT_DIR)/v2" \ +# -O zz_generated.deepcopy \ +# --go-header-file "$(BOILERPLATE)" \ +# --output-base "${GOPATH}/src" + +# When 'make gen.run' is executed, the previously generated files are actually cleaned up and then automatically generated separately +## gen.run: gen.deepcopy gen.docgo +.PHONY: gen.run +gen.run: gen.clean gen.deepcopyV1 gen.deepcopyV2 #gen.docs + +BOILERPLATE := $(ROOT_DIR)/scripts/boilerplate.go.txt + +INPUT_DIR := ./types/api/v1 +INPUT_DIRV2 := ./types/api/v2 +OUTPUT_DIRS := $(ROOT_DIR)/types/api/v1/../../.. +OUTPUT_DIRSV2 := $(ROOT_DIR)/types/api/v2/../../.. + +# TODO: output-base: $(ROOT_PACKAGE)/types/api/v2/...... +## gen.deepcopy: generate deepcopy v1 code +.PHONY: gen.deepcopyV1 +gen.deepcopyV1: tools.verify.deepcopy-gen + @echo "===========> Generating deepcopy go source files in $(INPUT_DIRS)" + @$(TOOLS_DIR)/deepcopy-gen \ + --input-dirs="$(INPUT_DIR)" \ + --output-file-base zz_generated.deepcopy \ + --go-header-file "$(BOILERPLATE)" \ + --output-base "$(OUTPUT_DIRS)" + +## gen.deepcopyV2: generate deepcopy v2 code +.PHONY: gen.deepcopyV2 +gen.deepcopyV2: tools.verify.deepcopy-gen + @echo "===========> Generating deepcopy go source files in $(INPUT_DIRSV2)" + @$(TOOLS_DIR)/deepcopy-gen \ + --input-dirs="$(INPUT_DIRV2)" \ + --output-file-base zz_generated.deepcopy \ + --go-header-file "$(BOILERPLATE)" \ + --output-base "$(OUTPUT_DIRSV2)" + +## gen.docgo: generate doc.go +.PHONY: gen.docs +gen.docs: go.build + @echo "===========> Generating deep of documents use $(BIN_DIR)/$(PLATFORM)/sealer gen-doc" + @$(BIN_DIR)/$(PLATFORM)/sealer gen-doc + +## gen.clean: clean the previously generated files +.PHONY: gen.clean +gen.clean: + @echo "===========> Delete $(INPUT_DIR)..." + @find $(INPUT_DIR) -type f -name '*_generated.*.go' -delete + @echo "===========> Delete $(INPUT_DIRSV2)" + @find $(INPUT_DIRV2) -type f -name '*_generated.*.go' -delete + +## gen.help: show help for gen +.PHONY: gen.help +gen.help: scripts/make-rules/gen.mk + $(call smallhelp) \ No newline at end of file diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk new file mode 100644 index 00000000000..1b1462a2311 --- /dev/null +++ b/scripts/make-rules/golang.mk @@ -0,0 +1,215 @@ +# Copyright © 2022 Alibaba Group Holding Ltd. +# +# 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. + +# ============================================================================== +# Build management helpers. These functions help to set, save and load the +# + +GO := go +# ! go 1.8 some packages fail to be pulled out. You are advised to use the gvm switchover version of the tools toolkit +GO_SUPPORTED_VERSIONS ?= |1.17|1.18|1.19|1.20| + +GO_LDFLAGS += -X $(VERSION_PACKAGE).gitVersion=$(GIT_TAG) \ + -X $(VERSION_PACKAGE).gitCommit=$(GIT_COMMIT) \ + -X $(VERSION_PACKAGE).gitTreeState=$(GIT_TREE_STATE) \ + -X $(VERSION_PACKAGE).buildDate=$(BUILD_DATE) \ + -s -w # -s -w deletes debugging information and symbol tables +ifeq ($(DEBUG), 1) + GO_BUILD_FLAGS += -gcflags "all=-N -l" + GO_LDFLAGS= +endif +# Fixed container dependency issues +# Link -> https://github.com/containers/image/pull/271/files +# For btrfs, we are currently only using overlays, so we don't need to include btrfs, otherwise we need to fix some lib issues +GO_BUILD_FLAGS += -tags "containers_image_openpgp exclude_graphdriver_devicemapper exclude_graphdriver_btrfs" -trimpath -ldflags "$(GO_LDFLAGS)" + +ifeq ($(ROOT_PACKAGE),) + $(error the variable ROOT_PACKAGE must be set prior to including golang.mk, ->/Makefile) +endif + +GOPATH ?= $(shell go env GOPATH) +ifeq ($(origin GOBIN), undefined) + GOBIN := $(GOPATH)/bin +endif + +# COMMANDS is Specify all files under ${ROOT_DIR}/cmd/ except those ending in.md +COMMANDS ?= $(filter-out %.md, $(wildcard ${ROOT_DIR}/cmd/*)) +ifeq (${COMMANDS},) + $(error Could not determine COMMANDS, set ROOT_DIR or run in source dir) +endif + +# BINS is the name of each file in ${COMMANDS}, excluding the directory path +# If there are no files in ${COMMANDS}, or if all files end in.md, ${BINS} will be empty +BINS ?= $(foreach cmd,${COMMANDS},$(notdir ${cmd})) +ifeq (${BINS},) + $(error Could not determine BINS, set ROOT_DIR or run in source dir) +endif + +# TODO: EXCLUDE_TESTS variable, which contains the name of the package to be excluded from the test +EXCLUDE_TESTS := github.com/sealerio/sealer/test github.com/sealerio/sealer/pkg/logger + +# ============================================================================== +# make: Nothing to be done build and build sub targets +# _output $(OUTPUT_DIR) +# ├── assets +# │ ├── sealer-unknown-linux-amd64.tar.gz +# │ ├── sealer-unknown-linux-amd64.tar.gz.sha256sum +# │ ├── seautil-unknown-linux-amd64.tar.gz +# │ └── seautil-unknown-linux-amd64.tar.gz.sha256sum +# └── bin $(BIN_DIR) +# ├── sealer +# │ └── linux_amd64 +# │ │ └── sealer +# │ └── linux_arm64 +# │ └── sealer +# └── seautil +# └── linux_amd64 +# └── seautil +# COMMAND=sealer +# PLATFORM=linux_amd64 +# OS=linux +# ARCH=amd64 +# BINS=sealer seautil +# BIN_DIR=/root/workspaces/sealer/_output/bin +# ============================================================================== + +## go.build.verify: Verify that a suitable version of Go exists +.PHONY: go.build.verify +go.build.verify: + @echo "===========> Verify that a suitable version of Go exists, current version: $(shell $(GO) version)" +ifneq ($(shell $(GO) version | grep -q -E '\bgo($(GO_SUPPORTED_VERSIONS))\b' && echo 0 || echo 1), 0) + $(error unsupported go version. Please make install one of the following supported version: '$(GO_SUPPORTED_VERSIONS)'.You can use make install. gvm to install gvm and switch to the specified version) +endif + +## go.bin.%: Verify binary for specific platform +.PHONY: go.bin.% +go.bin.%: + $(eval COMMAND := $(word 2,$(subst ., ,$*))) + $(eval PLATFORM := $(word 1,$(subst ., ,$*))) + @echo "===========> Verifying binary $(COMMAND) $(VERSION) for $(PLATFORM)" + @if [ ! -f $(BIN_DIR)/$(COMMAND)/$(PLATFORM)/$(COMMAND) ]; then echo $(MAKE) go.build PLATFORM=$(PLATFORM); fi + +## go.build.%: Build binary for specific platform +.PHONY: go.build.% +go.build.%: + $(eval COMMAND := $(word 2,$(subst ., ,$*))) + $(eval PLATFORM := $(word 1,$(subst ., ,$*))) + $(eval OS := $(word 1,$(subst _, ,$(PLATFORM)))) + $(eval ARCH := $(word 2,$(subst _, ,$(PLATFORM)))) + @echo "=====> COMMAND=$(COMMAND)" + @echo "=====> PLATFORM=$(PLATFORM)" + @echo "=====> BIN_DIR=$(BIN_DIR)" + @echo "===========> Building binary $(COMMAND) $(VERSION) for $(OS)_$(ARCH)" + @mkdir -p $(BIN_DIR)/$(COMMAND)/$(PLATFORM) + @if [ "$(COMMAND)" == "sealer" ] || [ "$(COMMAND)" == "seautil" ]; then \ + CGO_ENABLED=1; \ + CC=x86_64-linux-gnu-gcc; \ + if [ "$(ARCH)" == "arm64" ]; then \ + CC=aarch-linux-gnu-gcc; \ + fi; \ + CGO_ENABLED=$$CGO_ENABLED CC=$$CC GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o $(BIN_DIR)/$(COMMAND)/$(PLATFORM) $(ROOT_PACKAGE)/cmd/$(COMMAND); \ + else \ + CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o $(BIN_DIR)/$(COMMAND)/$(PLATFORM) -mod vendor $(ROOT_PACKAGE)/cmd/$(COMMAND); \ + fi + +## go.build: Build binaries +.PHONY: go.build +go.build: go.build.verify $(addprefix go.build., $(addprefix $(PLATFORM)., $(BINS))) + @echo "===========> Building binary $(BINS) $(VERSION) for $(PLATFORM)" + +## go.multiarch: Build multi-arch binaries +.PHONY: go.build.multiarch +go.build.multiarch: go.build.verify $(foreach p,$(PLATFORMS),$(addprefix go.build., $(addprefix $(p)., $(BINS)))) + +## go.linux-a: Build the project with a build script, use: ./scripts/build.sh -h +.PHONY: go.linux-a +go.linux-a: + @chmod +x $(BUILD_SCRIPTS) + @$(BUILD_SCRIPTS) -a + +## go.linux.%: Build linux_amd64 OR linux_arm64 binaries +.PHONY: go.linux.% +go.linux.%: + @echo "Building sealer and seautil binaries for Linux $*" + @chmod +x $(BUILD_SCRIPTS);GOOS=linux GOARCH=$* $(BUILD_SCRIPTS) -p linux/$* + @echo "$(shell go version)" + @echo "===========> Building binary for Linux $* $(BUILDAPP) *[Git Info]: $(VERSION)-$(GIT_TAG)-$(GIT_COMMIT)" + +## go.lint: Run golangci to lint source codes +.PHONY: go.lint +go.lint: tools.verify.golangci-lint + @echo "===========> Run golangci to lint source codes" + @$(TOOLS_DIR)/golangci-lint run -c $(ROOT_DIR)/.golangci.yml $(ROOT_DIR)/... + +## go.test: Run unit test +.PHONY: go.test +go.test: + @$(GO) test $(GO_BUILD_FLAGS) ./... + +## go.test.junit-report: Run unit test +.PHONY: go.test.junit-report +go.test.junit-report: tools.verify.go-junit-report + @echo "===========> Run unit test > $(TMP_DIR)/report.xml" + @$(GO) test -v -coverprofile=$(TMP_DIR)/coverage.out 2>&1 $(GO_BUILD_FLAGS) ./... | $(TOOLS_DIR)/go-junit-report -set-exit-code > $(TMP_DIR)/report.xml + @sed -i '/mock_.*.go/d' $(TMP_DIR)/coverage.out + @echo "===========> Test coverage of Go code is reported to $(TMP_DIR)/coverage.html by generating HTML" + @$(GO) tool cover -html=$(TMP_DIR)/coverage.out -o $(TMP_DIR)/coverage.html + +# .PHONY: go.test.junit-report +# go.test.junit-report: tools.verify.go-junit-report +# @echo "===========> Run unit test" +# @$(GO) test -race -cover -coverprofile=$(OUTPUT_DIR)/coverage.out \ +# -timeout=10m -shuffle=on -short -v `go list ./pkg/hook/ |\ +# egrep -v $(subst $(SPACE),'|',$(sort $(EXCLUDE_TESTS)))` 2>&1 | \ +# tee >(go-junit-report --set-exit-code >$(OUTPUT_DIR)/report.xml) +# @sed -i '/mock_.*.go/d' $(OUTPUT_DIR)/coverage.out # remove mock_.*.go files from test coverage +# @$(GO) tool cover -html=$(OUTPUT_DIR)/coverage.out -o $(OUTPUT_DIR)/coverage.html + +## go.test.cover: Run unit test with coverage +.PHONY: go.test.cover +go.test.cover: go.test.junit-report + @touch $(TMP_DIR)/coverage.out + @$(GO) tool cover -func=$(TMP_DIR)/coverage.out | \ + awk -v target=$(COVERAGE) -f $(ROOT_DIR)/scripts/coverage.awk + +## go.format: Run unit test and format codes +.PHONY: go.format +go.format: tools.verify.golines tools.verify.goimports + @echo "===========> Formating codes" + @$(FIND) -type f -name '*.go' | $(XARGS) gofmt -s -w + @$(FIND) -type f -name '*.go' | $(XARGS) $(TOOLS_DIR)/goimports -w -local $(ROOT_PACKAGE) + @$(FIND) -type f -name '*.go' | $(XARGS) $(TOOLS_DIR)/golines -w --max-len=120 --reformat-tags --shorten-comments --ignore-generated . + @$(GO) mod edit -fmt + +## imports: task to automatically handle import packages in Go files using goimports tool +.PHONY: go.imports +go.imports: tools.verify.goimports + @$(TOOLS_DIR)/goimports -l -w $(SRC) + +## go.updates: Check for updates to go.mod dependencies +.PHONY: go.updates +go.updates: tools.verify.go-mod-outdated + @$(GO) list -u -m -json all | go-mod-outdated -update -direct + +## go.clean: Clean all builds directories and files +.PHONY: go.clean +go.clean: + @echo "===========> Cleaning all builds OUTPUT_DIR($(OUTPUT_DIR)) AND BIN_DIR($(BIN_DIR))" + @-rm -vrf $(OUTPUT_DIR) $(BIN_DIR) + @echo "===========> End clean..." + +## copyright.help: Show copyright help +.PHONY: go.help +go.help: scripts/make-rules/golang.mk + $(call smallhelp) diff --git a/scripts/make-rules/image.mk b/scripts/make-rules/image.mk new file mode 100644 index 00000000000..efebf46984b --- /dev/null +++ b/scripts/make-rules/image.mk @@ -0,0 +1,155 @@ +# Copyright © 2022 Alibaba Group Holding Ltd. +# +# 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. + +# ============================================================================== +# Makefile helper functions for docker image +# TODO: For the time being only used for compilation, it can be arm or amd, please do not delete it, it can be extended with new functions +# ============================================================================== +# Path: scripts/make-rules/image.mk +# docker registry: registry.example.com/namespace/image:tag as: registry.hub.docker.com/cubxxw/: +# + +DOCKER := docker +DOCKER_SUPPORTED_API_VERSION ?= 1.32|1.40 + +REGISTRY_PREFIX ?= cubxxw +BASE_IMAGE = centos:centos8 + +EXTRA_ARGS ?= --no-cache +_DOCKER_BUILD_EXTRA_ARGS := + +ifdef HTTP_PROXY +_DOCKER_BUILD_EXTRA_ARGS += --build-arg HTTP_PROXY=${HTTP_PROXY} +endif + +ifneq ($(EXTRA_ARGS), ) +_DOCKER_BUILD_EXTRA_ARGS += $(EXTRA_ARGS) +endif + +# Determine image files by looking into build/docker/*/Dockerfile +IMAGES_DIR ?= $(wildcard ${ROOT_DIR}/build/docker/*) +# Determine images names by stripping out the dir names +IMAGES ?= $(filter-out tools,$(foreach image,${IMAGES_DIR},$(notdir ${image}))) + +# ifeq (${IMAGES},) +# $(error Could not determine IMAGES, set ROOT_DIR or run in source dir) +# endif + +# ============================================================================== +# Image targets +# ============================================================================== + +## image.verify: Verify docker version +.PHONY: image.verify +image.verify: + $(eval API_VERSION := $(shell $(DOCKER) version | grep -E 'API version: {1,6}[0-9]' | head -n1 | awk '{print $$3} END { if (NR==0) print 0}' )) + $(eval PASS := $(shell echo "$(API_VERSION) > $(DOCKER_SUPPORTED_API_VERSION)" | bc)) + @if [ $(PASS) -ne 1 ]; then \ + $(DOCKER) -v ;\ + echo "Unsupported docker version. Docker API version should be greater than $(DOCKER_SUPPORTED_API_VERSION)"; \ + exit 1; \ + fi + +## image.daemon.verify: Verify docker daemon experimental features +.PHONY: image.daemon.verify +image.daemon.verify: + $(eval PASS := $(shell $(DOCKER) version | grep -q -E 'Experimental: {1,5}true' && echo 1 || echo 0)) + @if [ $(PASS) -ne 1 ]; then \ + echo "Experimental features of Docker daemon is not enabled. Please add \"experimental\": true in '/etc/docker/daemon.json' and then restart Docker daemon."; \ + exit 1; \ + fi + +## image.build: Build docker images +.PHONY: image.build +image.build: image.verify go.build.verify $(addprefix image.build., $(addprefix $(IMAGE_PLAT)., $(IMAGES))) + +## image.build.multiarch: Build docker images for all platforms +.PHONY: image.build.multiarch +image.build.multiarch: image.verify go.build.verify $(foreach p,$(PLATFORMS),$(addprefix image.build., $(addprefix $(p)., $(IMAGES)))) + +## image.build.%: Build docker image for a specific platform +.PHONY: image.build.% +image.build.%: go.build.% + $(eval IMAGE := $(COMMAND)) + $(eval IMAGE_PLAT := $(subst _,/,$(PLATFORM))) + @echo "===========> Building docker image $(IMAGE) $(VERSION) for $(IMAGE_PLAT)" + @mkdir -p $(TMP_DIR)/$(IMAGE) + @cat $(ROOT_DIR)/build/docker/$(IMAGE)/Dockerfile\ + | sed "s#BASE_IMAGE#$(BASE_IMAGE)#g" >$(TMP_DIR)/$(IMAGE)/Dockerfile + @cp $(OUTPUT_DIR)/platforms/$(IMAGE_PLAT)/$(IMAGE) $(TMP_DIR)/$(IMAGE)/ + @DST_DIR=$(TMP_DIR)/$(IMAGE) $(ROOT_DIR)/build/docker/$(IMAGE)/build.sh 2>/dev/null || true + $(eval BUILD_SUFFIX := $(_DOCKER_BUILD_EXTRA_ARGS) --pull -t $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) $(TMP_DIR)/$(IMAGE)) + @if [ $(shell $(GO) env GOARCH) != $(ARCH) ] ; then \ + $(MAKE) image.daemon.verify ;\ + $(DOCKER) build --platform $(IMAGE_PLAT) $(BUILD_SUFFIX) ; \ + else \ + $(DOCKER) build $(BUILD_SUFFIX) ; \ + fi + @rm -rf $(TMP_DIR)/$(IMAGE) + +## image.push: Push docker images +.PHONY: image.push +image.push: image.verify go.build.verify $(addprefix image.push., $(addprefix $(IMAGE_PLAT)., $(IMAGES))) + +## image.push.multiarch: Push docker images for all platforms +.PHONY: image.push.multiarch +image.push.multiarch: image.verify go.build.verify $(foreach p,$(PLATFORMS),$(addprefix image.push., $(addprefix $(p)., $(IMAGES)))) + +## image.push.%: Push docker image for a specific platform +.PHONY: image.push.% +image.push.%: image.build.% + @echo "===========> Pushing image $(IMAGE) $(VERSION) to $(REGISTRY_PREFIX)" + $(DOCKER) push $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) + +## image.manifest.push: Push manifest list for multi-arch images +.PHONY: image.manifest.push +image.manifest.push: export DOCKER_CLI_EXPERIMENTAL := enabled +image.manifest.push: image.verify go.build.verify \ +$(addprefix image.manifest.push., $(addprefix $(IMAGE_PLAT)., $(IMAGES))) + +## image.manifest.push.%: Push manifest list for multi-arch images for a specific platform +.PHONY: image.manifest.push.% +image.manifest.push.%: image.push.% image.manifest.remove.% + @echo "===========> Pushing manifest $(IMAGE) $(VERSION) to $(REGISTRY_PREFIX) and then remove the local manifest list" + @$(DOCKER) manifest create $(REGISTRY_PREFIX)/$(IMAGE):$(VERSION) \ + $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) + @$(DOCKER) manifest annotate $(REGISTRY_PREFIX)/$(IMAGE):$(VERSION) \ + $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) \ + --os $(OS) --arch ${ARCH} + @$(DOCKER) manifest push --purge $(REGISTRY_PREFIX)/$(IMAGE):$(VERSION) + +# Docker cli has a bug: https://github.com/docker/cli/issues/954 +# If you find your manifests were not updated, +# Please manually delete them in $HOME/.docker/manifests/ +# and re-run. +## image.manifest.remove.%: Remove local manifest list +.PHONY: image.manifest.remove.% +image.manifest.remove.%: + @rm -rf ${HOME}/.docker/manifests/docker.io_$(REGISTRY_PREFIX)_$(IMAGE)-$(VERSION) + +## image.manifest.push.multiarch: Push manifest list for multi-arch images for all platforms +.PHONY: image.manifest.push.multiarch +image.manifest.push.multiarch: image.push.multiarch $(addprefix image.manifest.push.multiarch., $(IMAGES)) + +## image.manifest.push.multiarch.%: Push manifest list for multi-arch images for all platforms for a specific image +.PHONY: image.manifest.push.multiarch.% +image.manifest.push.multiarch.%: + @echo "===========> Pushing manifest $* $(VERSION) to $(REGISTRY_PREFIX) and then remove the local manifest list" + REGISTRY_PREFIX=$(REGISTRY_PREFIX) PLATFROMS="$(PLATFORMS)" IMAGE=$* VERSION=$(VERSION) DOCKER_CLI_EXPERIMENTAL=enabled \ + $(ROOT_DIR)/build/lib/create-manifest.sh + +## image.help: Print help for image targets +.PHONY: image.help +image.help: scripts/make-rules/image.mk + $(call smallhelp) \ No newline at end of file diff --git a/scripts/make-rules/tools.mk b/scripts/make-rules/tools.mk new file mode 100644 index 00000000000..96173297d3a --- /dev/null +++ b/scripts/make-rules/tools.mk @@ -0,0 +1,202 @@ +# Copyright © 2022 Alibaba Group Holding Ltd. +# +# 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. + +# ============================================================================== +# Makefile helper functions for tools(https://github.com/avelino/awesome-go) -> DIR: {TOOT_DIR}/tools | (go >= 1.19) +# Why download to the tools directory, thinking we might often switch Go versions using gvm. +# + +# sealer build use BUILD_TOOLS +BUILD_TOOLS ?= golangci-lint goimports addlicense deepcopy-gen conversion-gen ginkgo go-junit-report +# Code analysis tools +ANALYSIS_TOOLS = golangci-lint goimports golines go-callvis kube-score +# Code generation tools +GENERATION_TOOLS = deepcopy-gen conversion-gen protoc-gen-go cfssl rts codegen +# Testing tools +TEST_TOOLS = ginkgo go-junit-report gotests +# Version control tools +VERSION_CONTROL_TOOLS = addlicense go-gitlint git-chglog github-release gsemver +# Utility tools +UTILITY_TOOLS = go-mod-outdated mockgen gothanks richgo kubeconform +# All tools +ALL_TOOLS ?= $(ANALYSIS_TOOLS) $(GENERATION_TOOLS) $(TEST_TOOLS) $(VERSION_CONTROL_TOOLS) $(UTILITY_TOOLS) + +## tools.install: Install a must tools +.PHONY: tools.install +tools.install: $(addprefix tools.install., $(BUILD_TOOLS)) + +## tools.install-all: Install all tools +.PHONY: tools.install-all +tools.install-all: $(addprefix tools.install-all., $(ALL_TOOLS)) + +## tools.install.%: Install a single tool in $GOBIN/ +.PHONY: tools.install.% +tools.install.%: + @echo "===========> Installing $,The default installation path is $(GOBIN)/$*" + @$(MAKE) install.$* + +## tools.install-all.%: Parallelism install a single tool in ./tools/* +.PHONY: tools.install-all.% +tools.install-all.%: + @echo "===========> Installing $,The default installation path is $(TOOLS_DIR)/$*" + @$(MAKE) -j $(nproc) install.$* + +## tools.verify.%: Check if a tool is installed and install it +.PHONY: tools.verify.% +tools.verify.%: + @echo "===========> Verifying $* is installed" + @if [ ! -f $(TOOLS_DIR)/$* ]; then GOBIN=$(TOOLS_DIR) $(MAKE) tools.install.$*; fi + @echo "===========> $* is install in $(TOOLS_DIR)/$*" + +.PHONY: +## install.golangci-lint: Install golangci-lint +install.golangci-lint: + @$(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + +## install.goimports: Install goimports, used to format go source files +.PHONY: install.goimports +install.goimports: + @$(GO) install golang.org/x/tools/cmd/goimports@latest + +# Actions path: https://github.com/sealerio/sealer/tree/main/.github/workflows/go.yml#L37-L50 +## install.addlicense: Install addlicense, used to add license header to source files +.PHONY: install.addlicense +install.addlicense: + @$(GO) install github.com/google/addlicense@latest + +## install.deepcopy-gen: Install deepcopy-gen, used to generate deep copy functions +.PHONY: install.deepcopy-gen +install.deepcopy-gen: + @$(GO) install k8s.io/code-generator/cmd/deepcopy-gen@latest + +## install.conversion-gen: Install conversion-gen, used to generate conversion functions +.PHONY: install.conversion-gen +install.conversion-gen: + @$(GO) install k8s.io/code-generator/cmd/conversion-gen@latest + +## install.ginkgo: Install ginkgo to run a single test or set of tests +.PHONY: install.ginkgo +install.ginkgo: + @$(GO) install github.com/onsi/ginkgo/ginkgo@v1.16.2 + +## install.go-junit-report: Install go-junit-report, used to convert go test output to junit xml +.PHONY: install.go-junit-report +install.go-junit-report: + @$(GO) install github.com/jstemmer/go-junit-report@latest + +# ============================================================================== +# Tools that might be used include go gvm +# + +## install.kube-score: Install kube-score, used to check kubernetes yaml files +.PHONY: install.kube-score +install.kube-score: + @$(GO) install github.com/zegl/kube-score/cmd/kube-score@latest + +## install.kubeconform: Install kubeconform, used to check kubernetes yaml files +.PHONY: install.kubeconform +install.kubeconform: + @$(GO) install github.com/yannh/kubeconform/cmd/kubeconform@latest + +## Install go-gitlint: Install go-gitlint, used to check git commit message +.PHONY: install.go-gitlint +install.go-gitlint: + @$(GO) install github.com/marmotedu/go-gitlint/cmd/go-gitlint@latest + +## install.gsemver: Install gsemver, used to generate semver +.PHONY: install.gsemver +install.gsemver: + @$(GO) install github.com/arnaud-deprez/gsemver@latest + +## install.git-chglog: Install git-chglog, used to generate changelog +.PHONY: install.git-chglog +install.git-chglog: + @$(GO) install github.com/git-chglog/git-chglog/cmd/git-chglog@latest + +## install.github-release: Install github-release, used to create github release +.PHONY: install.github-release +install.github-release: + @$(GO) install github.com/github-release/github-release@latest + +## install.gvm: Install gvm, gvm is a Go version manager, built on top of the official go tool. +.PHONY: install.gvm +install.gvm: + @echo "===========> Installing gvm,The default installation path is ~/.gvm/scripts/gvm" + @bash < <(curl -s -S -L https://raw.gitee.com/moovweb/gvm/master/binscripts/gvm-installer) + @$(shell source /root/.gvm/scripts/gvm) + +## install.golines: Install golines, used to format long lines +.PHONY: install.golines +install.golines: + @$(GO) install github.com/segmentio/golines@latest + +## install.go-mod-outdated: Install go-mod-outdated, used to check outdated dependencies +.PHONY: install.go-mod-outdated +install.go-mod-outdated: + @$(GO) install github.com/psampaz/go-mod-outdated@latest + +## install.mockgen: Install mockgen, used to generate mock functions +.PHONY: install.mockgen +install.mockgen: + @$(GO) install github.com/golang/mock/mockgen@latest + +## install.gotests: Install gotests, used to generate test functions +.PHONY: install.gotests +install.gotests: + @$(GO) install github.com/cweill/gotests/gotests@latest + +## install.protoc-gen-go: Install protoc-gen-go, used to generate go source files from protobuf files +.PHONY: install.protoc-gen-go +install.protoc-gen-go: + @$(GO) install github.com/golang/protobuf/protoc-gen-go@latest + +## install.cfssl: Install cfssl, used to generate certificates +.PHONY: install.cfssl +install.cfssl: + @$(ROOT_DIR)/scripts/install/install.sh iam::install::install_cfssl + +## install.depth: Install depth, used to check dependency tree +.PHONY: install.depth +install.depth: + @$(GO) install github.com/KyleBanks/depth/cmd/depth@latest + +## install.go-callvis: Install go-callvis, used to visualize call graph +.PHONY: install.go-callvis +install.go-callvis: + @$(GO) install github.com/ofabry/go-callvis@latest + +## install.gothanks: Install gothanks, used to thank go dependencies +.PHONY: install.gothanks +install.gothanks: + @$(GO) install github.com/psampaz/gothanks@latest + +## install.richgo: Install richgo +.PHONY: install.richgo +install.richgo: + @$(GO) install github.com/kyoh86/richgo@latest + +## install.rts: Install rts +.PHONY: install.rts +install.rts: + @$(GO) install github.com/galeone/rts/cmd/rts@latest + +## install.codegen: Install code generator, used to generate code +.PHONY: install.codegen +install.codegen: + @$(GO) install ${ROOT_DIR}/tools/codegen/codegen.go + +## tools.help: Display help information about the tools package +.PHONY: tools.help +tools.help: scripts/make-rules/tools.mk + $(call smallhelp) \ No newline at end of file diff --git a/types/api/v1/zz_generated.deepcopy.go b/types/api/v1/zz_generated.deepcopy.go index d9ce093a594..e5b280976e7 100644 --- a/types/api/v1/zz_generated.deepcopy.go +++ b/types/api/v1/zz_generated.deepcopy.go @@ -20,7 +20,7 @@ package v1 import ( - "net" + net "net" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -236,7 +236,13 @@ func (in *Hosts) DeepCopyInto(out *Hosts) { if in.IPList != nil { in, out := &in.IPList, &out.IPList *out = make([]net.IP, len(*in)) - copy(*out, *in) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = make(net.IP, len(*in)) + copy(*out, *in) + } + } } return } @@ -279,6 +285,87 @@ func (in *Image) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageArg) DeepCopyInto(out *ImageArg) { + *out = *in + if in.Parent != nil { + in, out := &in.Parent, &out.Parent + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Current != nil { + in, out := &in.Current, &out.Current + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageArg. +func (in *ImageArg) DeepCopy() *ImageArg { + if in == nil { + return nil + } + out := new(ImageArg) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageCmd) DeepCopyInto(out *ImageCmd) { + *out = *in + if in.Parent != nil { + in, out := &in.Parent, &out.Parent + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Current != nil { + in, out := &in.Current, &out.Current + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageCmd. +func (in *ImageCmd) DeepCopy() *ImageCmd { + if in == nil { + return nil + } + out := new(ImageCmd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageConfig) DeepCopyInto(out *ImageConfig) { + *out = *in + in.Cmd.DeepCopyInto(&out.Cmd) + in.Args.DeepCopyInto(&out.Args) + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageConfig. +func (in *ImageConfig) DeepCopy() *ImageConfig { + if in == nil { + return nil + } + out := new(ImageConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ImageList) DeepCopyInto(out *ImageList) { *out = *in @@ -321,7 +408,7 @@ func (in *ImageSpec) DeepCopyInto(out *ImageSpec) { copy(*out, *in) } out.Platform = in.Platform - out.ImageConfig = in.ImageConfig + in.ImageConfig.DeepCopyInto(&out.ImageConfig) return } diff --git a/types/api/v2/zz_generated.deepcopy.go b/types/api/v2/zz_generated.deepcopy.go index dfa685a7bea..d89586a26ce 100644 --- a/types/api/v2/zz_generated.deepcopy.go +++ b/types/api/v2/zz_generated.deepcopy.go @@ -20,11 +20,27 @@ package v2 import ( - "net" + net "net" runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AppFile) DeepCopyInto(out *AppFile) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppFile. +func (in *AppFile) DeepCopy() *AppFile { + if in == nil { + return nil + } + out := new(AppFile) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Application) DeepCopyInto(out *Application) { *out = *in @@ -53,6 +69,32 @@ func (in *Application) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationConfig) DeepCopyInto(out *ApplicationConfig) { + *out = *in + if in.Files != nil { + in, out := &in.Files, &out.Files + *out = make([]AppFile, len(*in)) + copy(*out, *in) + } + if in.Launch != nil { + in, out := &in.Launch, &out.Launch + *out = new(Launch) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationConfig. +func (in *ApplicationConfig) DeepCopy() *ApplicationConfig { + if in == nil { + return nil + } + out := new(ApplicationConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApplicationList) DeepCopyInto(out *ApplicationList) { *out = *in @@ -89,6 +131,16 @@ func (in *ApplicationList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApplicationSpec) DeepCopyInto(out *ApplicationSpec) { *out = *in + if in.Cmds != nil { + in, out := &in.Cmds, &out.Cmds + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.LaunchApps != nil { + in, out := &in.LaunchApps, &out.LaunchApps + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Configs != nil { in, out := &in.Configs, &out.Configs *out = make([]ApplicationConfig, len(*in)) @@ -110,21 +162,17 @@ func (in *ApplicationSpec) DeepCopy() *ApplicationSpec { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ApplicationConfig) DeepCopyInto(out *ApplicationConfig) { +func (in *ApplicationStatus) DeepCopyInto(out *ApplicationStatus) { *out = *in - - out.Name = in.Name - out.Launch = in.Launch - return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationConfig. -func (in *ApplicationConfig) DeepCopy() *ApplicationConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationStatus. +func (in *ApplicationStatus) DeepCopy() *ApplicationStatus { if in == nil { return nil } - out := new(ApplicationConfig) + out := new(ApplicationStatus) in.DeepCopyInto(out) return out } @@ -198,6 +246,21 @@ func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.CMDArgs != nil { + in, out := &in.CMDArgs, &out.CMDArgs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.CMD != nil { + in, out := &in.CMD, &out.CMD + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.APPNames != nil { + in, out := &in.APPNames, &out.APPNames + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Hosts != nil { in, out := &in.Hosts, &out.Hosts *out = make([]Host, len(*in)) @@ -206,6 +269,15 @@ func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) { } } out.SSH = in.SSH + out.ContainerRuntime = in.ContainerRuntime + if in.HostAliases != nil { + in, out := &in.HostAliases, &out.HostAliases + *out = make([]HostAlias, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Registry.DeepCopyInto(&out.Registry) return } @@ -235,13 +307,99 @@ func (in *ClusterStatus) DeepCopy() *ClusterStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerRuntimeConfig) DeepCopyInto(out *ContainerRuntimeConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerRuntimeConfig. +func (in *ContainerRuntimeConfig) DeepCopy() *ContainerRuntimeConfig { + if in == nil { + return nil + } + out := new(ContainerRuntimeConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Delete) DeepCopyInto(out *Delete) { + *out = *in + if in.Cmds != nil { + in, out := &in.Cmds, &out.Cmds + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Delete. +func (in *Delete) DeepCopy() *Delete { + if in == nil { + return nil + } + out := new(Delete) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExternalRegistry) DeepCopyInto(out *ExternalRegistry) { + *out = *in + out.RegistryConfig = in.RegistryConfig + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalRegistry. +func (in *ExternalRegistry) DeepCopy() *ExternalRegistry { + if in == nil { + return nil + } + out := new(ExternalRegistry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Helm) DeepCopyInto(out *Helm) { + *out = *in + if in.ValueFiles != nil { + in, out := &in.ValueFiles, &out.ValueFiles + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Helm. +func (in *Helm) DeepCopy() *Helm { + if in == nil { + return nil + } + out := new(Helm) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Host) DeepCopyInto(out *Host) { *out = *in if in.IPS != nil { in, out := &in.IPS, &out.IPS *out = make([]net.IP, len(*in)) - copy(*out, *in) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = make(net.IP, len(*in)) + copy(*out, *in) + } + } } if in.Roles != nil { in, out := &in.Roles, &out.Roles @@ -254,6 +412,18 @@ func (in *Host) DeepCopyInto(out *Host) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Taints != nil { + in, out := &in.Taints, &out.Taints + *out = make([]string, len(*in)) + copy(*out, *in) + } return } @@ -266,3 +436,209 @@ func (in *Host) DeepCopy() *Host { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HostAlias) DeepCopyInto(out *HostAlias) { + *out = *in + if in.Hostnames != nil { + in, out := &in.Hostnames, &out.Hostnames + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostAlias. +func (in *HostAlias) DeepCopy() *HostAlias { + if in == nil { + return nil + } + out := new(HostAlias) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Kubectl) DeepCopyInto(out *Kubectl) { + *out = *in + if in.FileNames != nil { + in, out := &in.FileNames, &out.FileNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Kubectl. +func (in *Kubectl) DeepCopy() *Kubectl { + if in == nil { + return nil + } + out := new(Kubectl) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Launch) DeepCopyInto(out *Launch) { + *out = *in + if in.Cmds != nil { + in, out := &in.Cmds, &out.Cmds + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Launch. +func (in *Launch) DeepCopy() *Launch { + if in == nil { + return nil + } + out := new(Launch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalRegistry) DeepCopyInto(out *LocalRegistry) { + *out = *in + out.RegistryConfig = in.RegistryConfig + if in.HA != nil { + in, out := &in.HA, &out.HA + *out = new(bool) + **out = **in + } + if in.Insecure != nil { + in, out := &in.Insecure, &out.Insecure + *out = new(bool) + **out = **in + } + in.Cert.DeepCopyInto(&out.Cert) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalRegistry. +func (in *LocalRegistry) DeepCopy() *LocalRegistry { + if in == nil { + return nil + } + out := new(LocalRegistry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Registry) DeepCopyInto(out *Registry) { + *out = *in + if in.LocalRegistry != nil { + in, out := &in.LocalRegistry, &out.LocalRegistry + *out = new(LocalRegistry) + (*in).DeepCopyInto(*out) + } + if in.ExternalRegistry != nil { + in, out := &in.ExternalRegistry, &out.ExternalRegistry + *out = new(ExternalRegistry) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Registry. +func (in *Registry) DeepCopy() *Registry { + if in == nil { + return nil + } + out := new(Registry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RegistryConfig) DeepCopyInto(out *RegistryConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryConfig. +func (in *RegistryConfig) DeepCopy() *RegistryConfig { + if in == nil { + return nil + } + out := new(RegistryConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Shell) DeepCopyInto(out *Shell) { + *out = *in + if in.Envs != nil { + in, out := &in.Envs, &out.Envs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.FilePaths != nil { + in, out := &in.FilePaths, &out.FilePaths + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Shell. +func (in *Shell) DeepCopy() *Shell { + if in == nil { + return nil + } + out := new(Shell) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubjectAltName) DeepCopyInto(out *SubjectAltName) { + *out = *in + if in.DNSNames != nil { + in, out := &in.DNSNames, &out.DNSNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.IPs != nil { + in, out := &in.IPs, &out.IPs + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubjectAltName. +func (in *SubjectAltName) DeepCopy() *SubjectAltName { + if in == nil { + return nil + } + out := new(SubjectAltName) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSCert) DeepCopyInto(out *TLSCert) { + *out = *in + if in.SubjectAltName != nil { + in, out := &in.SubjectAltName, &out.SubjectAltName + *out = new(SubjectAltName) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCert. +func (in *TLSCert) DeepCopy() *TLSCert { + if in == nil { + return nil + } + out := new(TLSCert) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/fatih/color/LICENSE.md b/vendor/github.com/fatih/color/LICENSE.md new file mode 100644 index 00000000000..25fdaf639df --- /dev/null +++ b/vendor/github.com/fatih/color/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Fatih Arslan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/fatih/color/README.md b/vendor/github.com/fatih/color/README.md new file mode 100644 index 00000000000..5152bf59bf8 --- /dev/null +++ b/vendor/github.com/fatih/color/README.md @@ -0,0 +1,178 @@ +# color [![](https://github.com/fatih/color/workflows/build/badge.svg)](https://github.com/fatih/color/actions) [![PkgGoDev](https://pkg.go.dev/badge/github.com/fatih/color)](https://pkg.go.dev/github.com/fatih/color) + +Color lets you use colorized outputs in terms of [ANSI Escape +Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It +has support for Windows too! The API can be used in several ways, pick one that +suits you. + +![Color](https://user-images.githubusercontent.com/438920/96832689-03b3e000-13f4-11eb-9803-46f4c4de3406.jpg) + + +## Install + +```bash +go get github.com/fatih/color +``` + +## Examples + +### Standard colors + +```go +// Print with default helper functions +color.Cyan("Prints text in cyan.") + +// A newline will be appended automatically +color.Blue("Prints %s in blue.", "text") + +// These are using the default foreground colors +color.Red("We have red") +color.Magenta("And many others ..") + +``` + +### Mix and reuse colors + +```go +// Create a new color object +c := color.New(color.FgCyan).Add(color.Underline) +c.Println("Prints cyan text with an underline.") + +// Or just add them to New() +d := color.New(color.FgCyan, color.Bold) +d.Printf("This prints bold cyan %s\n", "too!.") + +// Mix up foreground and background colors, create new mixes! +red := color.New(color.FgRed) + +boldRed := red.Add(color.Bold) +boldRed.Println("This will print text in bold red.") + +whiteBackground := red.Add(color.BgWhite) +whiteBackground.Println("Red text with white background.") +``` + +### Use your own output (io.Writer) + +```go +// Use your own io.Writer output +color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + +blue := color.New(color.FgBlue) +blue.Fprint(writer, "This will print text in blue.") +``` + +### Custom print functions (PrintFunc) + +```go +// Create a custom print function for convenience +red := color.New(color.FgRed).PrintfFunc() +red("Warning") +red("Error: %s", err) + +// Mix up multiple attributes +notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() +notice("Don't forget this...") +``` + +### Custom fprint functions (FprintFunc) + +```go +blue := color.New(color.FgBlue).FprintfFunc() +blue(myWriter, "important notice: %s", stars) + +// Mix up with multiple attributes +success := color.New(color.Bold, color.FgGreen).FprintlnFunc() +success(myWriter, "Don't forget this...") +``` + +### Insert into noncolor strings (SprintFunc) + +```go +// Create SprintXxx functions to mix strings with other non-colorized strings: +yellow := color.New(color.FgYellow).SprintFunc() +red := color.New(color.FgRed).SprintFunc() +fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) + +info := color.New(color.FgWhite, color.BgGreen).SprintFunc() +fmt.Printf("This %s rocks!\n", info("package")) + +// Use helper functions +fmt.Println("This", color.RedString("warning"), "should be not neglected.") +fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") + +// Windows supported too! Just don't forget to change the output to color.Output +fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) +``` + +### Plug into existing code + +```go +// Use handy standard colors +color.Set(color.FgYellow) + +fmt.Println("Existing text will now be in yellow") +fmt.Printf("This one %s\n", "too") + +color.Unset() // Don't forget to unset + +// You can mix up parameters +color.Set(color.FgMagenta, color.Bold) +defer color.Unset() // Use it in your function + +fmt.Println("All text will now be bold magenta.") +``` + +### Disable/Enable color + +There might be a case where you want to explicitly disable/enable color output. the +`go-isatty` package will automatically disable color output for non-tty output streams +(for example if the output were piped directly to `less`). + +The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment +variable is set (regardless of its value). + +`Color` has support to disable/enable colors programatically both globally and +for single color definitions. For example suppose you have a CLI app and a +`--no-color` bool flag. You can easily disable the color output with: + +```go +var flagNoColor = flag.Bool("no-color", false, "Disable color output") + +if *flagNoColor { + color.NoColor = true // disables colorized output +} +``` + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + +```go +c := color.New(color.FgCyan) +c.Println("Prints cyan text") + +c.DisableColor() +c.Println("This is printed without any color") + +c.EnableColor() +c.Println("This prints again cyan...") +``` + +## GitHub Actions + +To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams. + +## Todo + +* Save/Return previous values +* Evaluate fmt.Formatter interface + + +## Credits + + * [Fatih Arslan](https://github.com/fatih) + * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) + +## License + +The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details diff --git a/vendor/github.com/fatih/color/color.go b/vendor/github.com/fatih/color/color.go new file mode 100644 index 00000000000..98a60f3c88d --- /dev/null +++ b/vendor/github.com/fatih/color/color.go @@ -0,0 +1,618 @@ +package color + +import ( + "fmt" + "io" + "os" + "strconv" + "strings" + "sync" + + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +var ( + // NoColor defines if the output is colorized or not. It's dynamically set to + // false or true based on the stdout's file descriptor referring to a terminal + // or not. It's also set to true if the NO_COLOR environment variable is + // set (regardless of its value). This is a global option and affects all + // colors. For more control over each color block use the methods + // DisableColor() individually. + NoColor = noColorExists() || os.Getenv("TERM") == "dumb" || + (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) + + // Output defines the standard output of the print functions. By default + // os.Stdout is used. + Output = colorable.NewColorableStdout() + + // Error defines a color supporting writer for os.Stderr. + Error = colorable.NewColorableStderr() + + // colorsCache is used to reduce the count of created Color objects and + // allows to reuse already created objects with required Attribute. + colorsCache = make(map[Attribute]*Color) + colorsCacheMu sync.Mutex // protects colorsCache +) + +// noColorExists returns true if the environment variable NO_COLOR exists. +func noColorExists() bool { + _, exists := os.LookupEnv("NO_COLOR") + return exists +} + +// Color defines a custom color object which is defined by SGR parameters. +type Color struct { + params []Attribute + noColor *bool +} + +// Attribute defines a single SGR Code +type Attribute int + +const escape = "\x1b" + +// Base attributes +const ( + Reset Attribute = iota + Bold + Faint + Italic + Underline + BlinkSlow + BlinkRapid + ReverseVideo + Concealed + CrossedOut +) + +// Foreground text colors +const ( + FgBlack Attribute = iota + 30 + FgRed + FgGreen + FgYellow + FgBlue + FgMagenta + FgCyan + FgWhite +) + +// Foreground Hi-Intensity text colors +const ( + FgHiBlack Attribute = iota + 90 + FgHiRed + FgHiGreen + FgHiYellow + FgHiBlue + FgHiMagenta + FgHiCyan + FgHiWhite +) + +// Background text colors +const ( + BgBlack Attribute = iota + 40 + BgRed + BgGreen + BgYellow + BgBlue + BgMagenta + BgCyan + BgWhite +) + +// Background Hi-Intensity text colors +const ( + BgHiBlack Attribute = iota + 100 + BgHiRed + BgHiGreen + BgHiYellow + BgHiBlue + BgHiMagenta + BgHiCyan + BgHiWhite +) + +// New returns a newly created color object. +func New(value ...Attribute) *Color { + c := &Color{ + params: make([]Attribute, 0), + } + + if noColorExists() { + c.noColor = boolPtr(true) + } + + c.Add(value...) + return c +} + +// Set sets the given parameters immediately. It will change the color of +// output with the given SGR parameters until color.Unset() is called. +func Set(p ...Attribute) *Color { + c := New(p...) + c.Set() + return c +} + +// Unset resets all escape attributes and clears the output. Usually should +// be called after Set(). +func Unset() { + if NoColor { + return + } + + fmt.Fprintf(Output, "%s[%dm", escape, Reset) +} + +// Set sets the SGR sequence. +func (c *Color) Set() *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(Output, c.format()) + return c +} + +func (c *Color) unset() { + if c.isNoColorSet() { + return + } + + Unset() +} + +func (c *Color) setWriter(w io.Writer) *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(w, c.format()) + return c +} + +func (c *Color) unsetWriter(w io.Writer) { + if c.isNoColorSet() { + return + } + + if NoColor { + return + } + + fmt.Fprintf(w, "%s[%dm", escape, Reset) +} + +// Add is used to chain SGR parameters. Use as many as parameters to combine +// and create custom color objects. Example: Add(color.FgRed, color.Underline). +func (c *Color) Add(value ...Attribute) *Color { + c.params = append(c.params, value...) + return c +} + +func (c *Color) prepend(value Attribute) { + c.params = append(c.params, 0) + copy(c.params[1:], c.params[0:]) + c.params[0] = value +} + +// Fprint formats using the default formats for its operands and writes to w. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprint(w, a...) +} + +// Print formats using the default formats for its operands and writes to +// standard output. Spaces are added between operands when neither is a +// string. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Print(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprint(Output, a...) +} + +// Fprintf formats according to a format specifier and writes to w. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintf(w, format, a...) +} + +// Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. +// This is the standard fmt.Printf() method wrapped with the given color. +func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintf(Output, format, a...) +} + +// Fprintln formats using the default formats for its operands and writes to w. +// Spaces are always added between operands and a newline is appended. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintln(w, a...) +} + +// Println formats using the default formats for its operands and writes to +// standard output. Spaces are always added between operands and a newline is +// appended. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Println(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintln(Output, a...) +} + +// Sprint is just like Print, but returns a string instead of printing it. +func (c *Color) Sprint(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) +} + +// Sprintln is just like Println, but returns a string instead of printing it. +func (c *Color) Sprintln(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) +} + +// Sprintf is just like Printf, but returns a string instead of printing it. +func (c *Color) Sprintf(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) +} + +// FprintFunc returns a new function that prints the passed arguments as +// colorized with color.Fprint(). +func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprint(w, a...) + } +} + +// PrintFunc returns a new function that prints the passed arguments as +// colorized with color.Print(). +func (c *Color) PrintFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Print(a...) + } +} + +// FprintfFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintf(). +func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) { + return func(w io.Writer, format string, a ...interface{}) { + c.Fprintf(w, format, a...) + } +} + +// PrintfFunc returns a new function that prints the passed arguments as +// colorized with color.Printf(). +func (c *Color) PrintfFunc() func(format string, a ...interface{}) { + return func(format string, a ...interface{}) { + c.Printf(format, a...) + } +} + +// FprintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintln(). +func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprintln(w, a...) + } +} + +// PrintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Println(). +func (c *Color) PrintlnFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Println(a...) + } +} + +// SprintFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprint(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output, example: +// +// put := New(FgYellow).SprintFunc() +// fmt.Fprintf(color.Output, "This is a %s", put("warning")) +func (c *Color) SprintFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) + } +} + +// SprintfFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintf(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { + return func(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) + } +} + +// SprintlnFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintln(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintlnFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) + } +} + +// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m" +// an example output might be: "1;36" -> bold cyan +func (c *Color) sequence() string { + format := make([]string, len(c.params)) + for i, v := range c.params { + format[i] = strconv.Itoa(int(v)) + } + + return strings.Join(format, ";") +} + +// wrap wraps the s string with the colors attributes. The string is ready to +// be printed. +func (c *Color) wrap(s string) string { + if c.isNoColorSet() { + return s + } + + return c.format() + s + c.unformat() +} + +func (c *Color) format() string { + return fmt.Sprintf("%s[%sm", escape, c.sequence()) +} + +func (c *Color) unformat() string { + return fmt.Sprintf("%s[%dm", escape, Reset) +} + +// DisableColor disables the color output. Useful to not change any existing +// code and still being able to output. Can be used for flags like +// "--no-color". To enable back use EnableColor() method. +func (c *Color) DisableColor() { + c.noColor = boolPtr(true) +} + +// EnableColor enables the color output. Use it in conjunction with +// DisableColor(). Otherwise this method has no side effects. +func (c *Color) EnableColor() { + c.noColor = boolPtr(false) +} + +func (c *Color) isNoColorSet() bool { + // check first if we have user set action + if c.noColor != nil { + return *c.noColor + } + + // if not return the global option, which is disabled by default + return NoColor +} + +// Equals returns a boolean value indicating whether two colors are equal. +func (c *Color) Equals(c2 *Color) bool { + if len(c.params) != len(c2.params) { + return false + } + + for _, attr := range c.params { + if !c2.attrExists(attr) { + return false + } + } + + return true +} + +func (c *Color) attrExists(a Attribute) bool { + for _, attr := range c.params { + if attr == a { + return true + } + } + + return false +} + +func boolPtr(v bool) *bool { + return &v +} + +func getCachedColor(p Attribute) *Color { + colorsCacheMu.Lock() + defer colorsCacheMu.Unlock() + + c, ok := colorsCache[p] + if !ok { + c = New(p) + colorsCache[p] = c + } + + return c +} + +func colorPrint(format string, p Attribute, a ...interface{}) { + c := getCachedColor(p) + + if !strings.HasSuffix(format, "\n") { + format += "\n" + } + + if len(a) == 0 { + c.Print(format) + } else { + c.Printf(format, a...) + } +} + +func colorString(format string, p Attribute, a ...interface{}) string { + c := getCachedColor(p) + + if len(a) == 0 { + return c.SprintFunc()(format) + } + + return c.SprintfFunc()(format, a...) +} + +// Black is a convenient helper function to print with black foreground. A +// newline is appended to format by default. +func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) } + +// Red is a convenient helper function to print with red foreground. A +// newline is appended to format by default. +func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) } + +// Green is a convenient helper function to print with green foreground. A +// newline is appended to format by default. +func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) } + +// Yellow is a convenient helper function to print with yellow foreground. +// A newline is appended to format by default. +func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) } + +// Blue is a convenient helper function to print with blue foreground. A +// newline is appended to format by default. +func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) } + +// Magenta is a convenient helper function to print with magenta foreground. +// A newline is appended to format by default. +func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) } + +// Cyan is a convenient helper function to print with cyan foreground. A +// newline is appended to format by default. +func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) } + +// White is a convenient helper function to print with white foreground. A +// newline is appended to format by default. +func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) } + +// BlackString is a convenient helper function to return a string with black +// foreground. +func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } + +// RedString is a convenient helper function to return a string with red +// foreground. +func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } + +// GreenString is a convenient helper function to return a string with green +// foreground. +func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } + +// YellowString is a convenient helper function to return a string with yellow +// foreground. +func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } + +// BlueString is a convenient helper function to return a string with blue +// foreground. +func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } + +// MagentaString is a convenient helper function to return a string with magenta +// foreground. +func MagentaString(format string, a ...interface{}) string { + return colorString(format, FgMagenta, a...) +} + +// CyanString is a convenient helper function to return a string with cyan +// foreground. +func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } + +// WhiteString is a convenient helper function to return a string with white +// foreground. +func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } + +// HiBlack is a convenient helper function to print with hi-intensity black foreground. A +// newline is appended to format by default. +func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) } + +// HiRed is a convenient helper function to print with hi-intensity red foreground. A +// newline is appended to format by default. +func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) } + +// HiGreen is a convenient helper function to print with hi-intensity green foreground. A +// newline is appended to format by default. +func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) } + +// HiYellow is a convenient helper function to print with hi-intensity yellow foreground. +// A newline is appended to format by default. +func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) } + +// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A +// newline is appended to format by default. +func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) } + +// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground. +// A newline is appended to format by default. +func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) } + +// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A +// newline is appended to format by default. +func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) } + +// HiWhite is a convenient helper function to print with hi-intensity white foreground. A +// newline is appended to format by default. +func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) } + +// HiBlackString is a convenient helper function to return a string with hi-intensity black +// foreground. +func HiBlackString(format string, a ...interface{}) string { + return colorString(format, FgHiBlack, a...) +} + +// HiRedString is a convenient helper function to return a string with hi-intensity red +// foreground. +func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) } + +// HiGreenString is a convenient helper function to return a string with hi-intensity green +// foreground. +func HiGreenString(format string, a ...interface{}) string { + return colorString(format, FgHiGreen, a...) +} + +// HiYellowString is a convenient helper function to return a string with hi-intensity yellow +// foreground. +func HiYellowString(format string, a ...interface{}) string { + return colorString(format, FgHiYellow, a...) +} + +// HiBlueString is a convenient helper function to return a string with hi-intensity blue +// foreground. +func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) } + +// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta +// foreground. +func HiMagentaString(format string, a ...interface{}) string { + return colorString(format, FgHiMagenta, a...) +} + +// HiCyanString is a convenient helper function to return a string with hi-intensity cyan +// foreground. +func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) } + +// HiWhiteString is a convenient helper function to return a string with hi-intensity white +// foreground. +func HiWhiteString(format string, a ...interface{}) string { + return colorString(format, FgHiWhite, a...) +} diff --git a/vendor/github.com/fatih/color/doc.go b/vendor/github.com/fatih/color/doc.go new file mode 100644 index 00000000000..04541de786f --- /dev/null +++ b/vendor/github.com/fatih/color/doc.go @@ -0,0 +1,135 @@ +/* +Package color is an ANSI color package to output colorized or SGR defined +output to the standard output. The API can be used in several way, pick one +that suits you. + +Use simple and default helper functions with predefined foreground colors: + + color.Cyan("Prints text in cyan.") + + // a newline will be appended automatically + color.Blue("Prints %s in blue.", "text") + + // More default foreground colors.. + color.Red("We have red") + color.Yellow("Yellow color too!") + color.Magenta("And many others ..") + + // Hi-intensity colors + color.HiGreen("Bright green color.") + color.HiBlack("Bright black means gray..") + color.HiWhite("Shiny white color!") + +However there are times where custom color mixes are required. Below are some +examples to create custom color objects and use the print functions of each +separate color object. + + // Create a new color object + c := color.New(color.FgCyan).Add(color.Underline) + c.Println("Prints cyan text with an underline.") + + // Or just add them to New() + d := color.New(color.FgCyan, color.Bold) + d.Printf("This prints bold cyan %s\n", "too!.") + + + // Mix up foreground and background colors, create new mixes! + red := color.New(color.FgRed) + + boldRed := red.Add(color.Bold) + boldRed.Println("This will print text in bold red.") + + whiteBackground := red.Add(color.BgWhite) + whiteBackground.Println("Red text with White background.") + + // Use your own io.Writer output + color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + + blue := color.New(color.FgBlue) + blue.Fprint(myWriter, "This will print text in blue.") + +You can create PrintXxx functions to simplify even more: + + // Create a custom print function for convenient + red := color.New(color.FgRed).PrintfFunc() + red("warning") + red("error: %s", err) + + // Mix up multiple attributes + notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() + notice("don't forget this...") + +You can also FprintXxx functions to pass your own io.Writer: + + blue := color.New(FgBlue).FprintfFunc() + blue(myWriter, "important notice: %s", stars) + + // Mix up with multiple attributes + success := color.New(color.Bold, color.FgGreen).FprintlnFunc() + success(myWriter, don't forget this...") + + +Or create SprintXxx functions to mix strings with other non-colorized strings: + + yellow := New(FgYellow).SprintFunc() + red := New(FgRed).SprintFunc() + + fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Printf("this %s rocks!\n", info("package")) + +Windows support is enabled by default. All Print functions work as intended. +However only for color.SprintXXX functions, user should use fmt.FprintXXX and +set the output to color.Output: + + fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) + +Using with existing code is possible. Just use the Set() method to set the +standard output to the given parameters. That way a rewrite of an existing +code is not required. + + // Use handy standard colors. + color.Set(color.FgYellow) + + fmt.Println("Existing text will be now in Yellow") + fmt.Printf("This one %s\n", "too") + + color.Unset() // don't forget to unset + + // You can mix up parameters + color.Set(color.FgMagenta, color.Bold) + defer color.Unset() // use it in your function + + fmt.Println("All text will be now bold magenta.") + +There might be a case where you want to disable color output (for example to +pipe the standard output of your app to somewhere else). `Color` has support to +disable colors both globally and for single color definition. For example +suppose you have a CLI app and a `--no-color` bool flag. You can easily disable +the color output with: + + var flagNoColor = flag.Bool("no-color", false, "Disable color output") + + if *flagNoColor { + color.NoColor = true // disables colorized output + } + +You can also disable the color by setting the NO_COLOR environment variable to any value. + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + + c := color.New(color.FgCyan) + c.Println("Prints cyan text") + + c.DisableColor() + c.Println("This is printed without any color") + + c.EnableColor() + c.Println("This prints again cyan...") +*/ +package color diff --git a/vendor/github.com/gosuri/uitable/.travis.yml b/vendor/github.com/gosuri/uitable/.travis.yml new file mode 100644 index 00000000000..26fc3b438bf --- /dev/null +++ b/vendor/github.com/gosuri/uitable/.travis.yml @@ -0,0 +1,6 @@ +language: go +sudo: false +install: + - go get ./... +go: + - tip diff --git a/vendor/github.com/gosuri/uitable/LICENSE b/vendor/github.com/gosuri/uitable/LICENSE new file mode 100644 index 00000000000..e436d90439d --- /dev/null +++ b/vendor/github.com/gosuri/uitable/LICENSE @@ -0,0 +1,10 @@ +MIT License +=========== + +Copyright (c) 2015, Greg Osuri + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/gosuri/uitable/Makefile b/vendor/github.com/gosuri/uitable/Makefile new file mode 100644 index 00000000000..60246a8a93c --- /dev/null +++ b/vendor/github.com/gosuri/uitable/Makefile @@ -0,0 +1,4 @@ +test: + @go test ./... + +.PHONY: test diff --git a/vendor/github.com/gosuri/uitable/README.md b/vendor/github.com/gosuri/uitable/README.md new file mode 100644 index 00000000000..683b538d132 --- /dev/null +++ b/vendor/github.com/gosuri/uitable/README.md @@ -0,0 +1,67 @@ +# uitable [![GoDoc](https://godoc.org/github.com/gosuri/uitable?status.svg)](https://godoc.org/github.com/gosuri/uitable) [![Build Status](https://travis-ci.org/gosuri/uitable.svg?branch=master)](https://travis-ci.org/gosuri/uitable) + +uitable is a go library for representing data as tables for terminal applications. It provides primitives for sizing and wrapping columns to improve readability. + +## Example Usage + +Full source code for the example is available at [example/main.go](example/main.go) + +```go +table := uitable.New() +table.MaxColWidth = 50 + +table.AddRow("NAME", "BIRTHDAY", "BIO") +for _, hacker := range hackers { + table.AddRow(hacker.Name, hacker.Birthday, hacker.Bio) +} +fmt.Println(table) +``` + +Will render the data as: + +```sh +NAME BIRTHDAY BIO +Ada Lovelace December 10, 1815 Ada was a British mathematician and writer, chi... +Alan Turing June 23, 1912 Alan was a British pioneering computer scientis... +``` + +For wrapping in two columns: + +```go +table = uitable.New() +table.MaxColWidth = 80 +table.Wrap = true // wrap columns + +for _, hacker := range hackers { + table.AddRow("Name:", hacker.Name) + table.AddRow("Birthday:", hacker.Birthday) + table.AddRow("Bio:", hacker.Bio) + table.AddRow("") // blank +} +fmt.Println(table) +``` + +Will render the data as: + +``` +Name: Ada Lovelace +Birthday: December 10, 1815 +Bio: Ada was a British mathematician and writer, chiefly known for her work on + Charles Babbage's early mechanical general-purpose computer, the Analytical + Engine + +Name: Alan Turing +Birthday: June 23, 1912 +Bio: Alan was a British pioneering computer scientist, mathematician, logician, + cryptanalyst and theoretical biologist +``` + +## Installation + +``` +$ go get -v github.com/gosuri/uitable +``` + + +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/gosuri/uitable/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + diff --git a/vendor/github.com/gosuri/uitable/table.go b/vendor/github.com/gosuri/uitable/table.go new file mode 100644 index 00000000000..b764e51502c --- /dev/null +++ b/vendor/github.com/gosuri/uitable/table.go @@ -0,0 +1,205 @@ +// Package uitable provides a decorator for formating data as a table +package uitable + +import ( + "fmt" + "strings" + "sync" + + "github.com/fatih/color" + "github.com/gosuri/uitable/util/strutil" + "github.com/gosuri/uitable/util/wordwrap" +) + +// Separator is the default column seperator +var Separator = "\t" + +// Table represents a decorator that renders the data in formatted in a table +type Table struct { + // Rows is the collection of rows in the table + Rows []*Row + + // MaxColWidth is the maximum allowed width for cells in the table + MaxColWidth uint + + // Wrap when set to true wraps the contents of the columns when the length exceeds the MaxColWidth + Wrap bool + + // Separator is the seperator for columns in the table. Default is "\t" + Separator string + + mtx *sync.RWMutex + rightAlign map[int]bool +} + +// New returns a new Table with default values +func New() *Table { + return &Table{ + Separator: Separator, + mtx: new(sync.RWMutex), + rightAlign: map[int]bool{}, + } +} + +// AddRow adds a new row to the table +func (t *Table) AddRow(data ...interface{}) *Table { + t.mtx.Lock() + defer t.mtx.Unlock() + r := NewRow(data...) + t.Rows = append(t.Rows, r) + return t +} + +// Bytes returns the []byte value of table +func (t *Table) Bytes() []byte { + return []byte(t.String()) +} + +func (t *Table) RightAlign(col int) { + t.mtx.Lock() + t.rightAlign[col] = true + t.mtx.Unlock() +} + +// String returns the string value of table +func (t *Table) String() string { + t.mtx.RLock() + defer t.mtx.RUnlock() + + if len(t.Rows) == 0 { + return "" + } + + // determine the width for each column (cell in a row) + var colwidths []uint + for _, row := range t.Rows { + for i, cell := range row.Cells { + // resize colwidth array + if i+1 > len(colwidths) { + colwidths = append(colwidths, 0) + } + cellwidth := cell.LineWidth() + if t.MaxColWidth != 0 && cellwidth > t.MaxColWidth { + cellwidth = t.MaxColWidth + } + + if cellwidth > colwidths[i] { + colwidths[i] = cellwidth + } + } + } + + var lines []string + for _, row := range t.Rows { + row.Separator = t.Separator + for i, cell := range row.Cells { + cell.Width = colwidths[i] + cell.Wrap = t.Wrap + cell.RightAlign = t.rightAlign[i] + } + lines = append(lines, row.String()) + } + return strutil.Join(lines, "\n") +} + +// Row represents a row in a table +type Row struct { + // Cells is the group of cell for the row + Cells []*Cell + + // Separator for tabular columns + Separator string +} + +// NewRow returns a new Row and adds the data to the row +func NewRow(data ...interface{}) *Row { + r := &Row{Cells: make([]*Cell, len(data))} + for i, d := range data { + r.Cells[i] = &Cell{Data: d} + } + return r +} + +// String returns the string representation of the row +func (r *Row) String() string { + // get the max number of lines for each cell + var lc int // line count + for _, cell := range r.Cells { + if clc := len(strings.Split(cell.String(), "\n")); clc > lc { + lc = clc + } + } + + // allocate a two-dimentional array of cells for each line and add size them + cells := make([][]*Cell, lc) + for x := 0; x < lc; x++ { + cells[x] = make([]*Cell, len(r.Cells)) + for y := 0; y < len(r.Cells); y++ { + cells[x][y] = &Cell{Width: r.Cells[y].Width} + } + } + + // insert each line in a cell as new cell in the cells array + for y, cell := range r.Cells { + lines := strings.Split(cell.String(), "\n") + for x, line := range lines { + cells[x][y].Data = line + } + } + + // format each line + lines := make([]string, lc) + for x := range lines { + line := make([]string, len(cells[x])) + for y := range cells[x] { + line[y] = cells[x][y].String() + } + lines[x] = strutil.Join(line, r.Separator) + } + return strings.Join(lines, "\n") +} + +// Cell represents a column in a row +type Cell struct { + // Width is the width of the cell + Width uint + + // Wrap when true wraps the contents of the cell when the lenght exceeds the width + Wrap bool + + // RightAlign when true aligns contents to the right + RightAlign bool + + // Data is the cell data + Data interface{} +} + +// LineWidth returns the max width of all the lines in a cell +func (c *Cell) LineWidth() uint { + width := 0 + for _, s := range strings.Split(c.String(), "\n") { + w := strutil.StringWidth(s) + if w > width { + width = w + } + } + return uint(width) +} + +// String returns the string formated representation of the cell +func (c *Cell) String() string { + if c.Data == nil { + return strutil.PadLeft(" ", int(c.Width), ' ') + } + col := color.New(color.FgBlack) + col.DisableColor() + s := fmt.Sprintf("%v", col.Sprint(c.Data)) + if c.Width > 0 { + if c.Wrap && uint(len(s)) > c.Width { + return wordwrap.WrapString(s, c.Width) + } else { + return strutil.Resize(s, c.Width, c.RightAlign) + } + } + return s +} diff --git a/vendor/github.com/gosuri/uitable/util/strutil/strutil.go b/vendor/github.com/gosuri/uitable/util/strutil/strutil.go new file mode 100644 index 00000000000..53f30a8fd3b --- /dev/null +++ b/vendor/github.com/gosuri/uitable/util/strutil/strutil.go @@ -0,0 +1,101 @@ +// Package strutil provides various utilities for manipulating strings +package strutil + +import ( + "bytes" + "regexp" + + "github.com/mattn/go-runewidth" +) + +const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" + +var re = regexp.MustCompile(ansi) + +// PadRight returns a new string of a specified length in which the end of the current string is padded with spaces or with a specified Unicode character. +func PadRight(str string, length int, pad byte) string { + slen := StringWidth(str) + if slen >= length { + return str + } + buf := bytes.NewBufferString(str) + for i := 0; i < length-slen; i++ { + buf.WriteByte(pad) + } + return buf.String() +} + +// PadLeft returns a new string of a specified length in which the beginning of the current string is padded with spaces or with a specified Unicode character. +func PadLeft(str string, length int, pad byte) string { + slen := StringWidth(str) + if slen >= length { + return str + } + var buf bytes.Buffer + for i := 0; i < length-slen; i++ { + buf.WriteByte(pad) + } + buf.WriteString(str) + return buf.String() +} + +// Resize resizes the string with the given length. It ellipses with '...' when the string's length exceeds +// the desired length or pads spaces to the right of the string when length is smaller than desired +func Resize(s string, length uint, rightAlign bool) string { + slen := StringWidth(s) + n := int(length) + if slen == n { + return s + } + // Pads only when length of the string smaller than len needed + if rightAlign { + s = PadLeft(s, n, ' ') + } else { + s = PadRight(s, n, ' ') + } + if slen > n { + rs := []rune(s) + var buf bytes.Buffer + w := 0 + for _, r := range rs { + buf.WriteRune(r) + rw := RuneWidth(r) + if w+rw >= n-3 { + break + } + w += rw + } + buf.WriteString("...") + s = buf.String() + } + return s +} + +// Join joins the list of the string with the delim provided. +// Returns an empty string for empty list +func Join(list []string, delim string) string { + if len(list) == 0 { + return "" + } + var buf bytes.Buffer + for i := 0; i < len(list)-1; i++ { + buf.WriteString(list[i] + delim) + } + buf.WriteString(list[len(list)-1]) + return buf.String() +} + +// Strip strips the string of all colors +func Strip(s string) string { + return re.ReplaceAllString(s, "") +} + +// StringWidth returns the actual width of the string without colors +func StringWidth(s string) int { + return runewidth.StringWidth(Strip(s)) +} + +// RuneWidth returns the actual width of the rune +func RuneWidth(s rune) int { + return runewidth.RuneWidth(s) +} diff --git a/vendor/github.com/gosuri/uitable/util/wordwrap/LICENSE.md b/vendor/github.com/gosuri/uitable/util/wordwrap/LICENSE.md new file mode 100644 index 00000000000..22985159044 --- /dev/null +++ b/vendor/github.com/gosuri/uitable/util/wordwrap/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/gosuri/uitable/util/wordwrap/README.md b/vendor/github.com/gosuri/uitable/util/wordwrap/README.md new file mode 100644 index 00000000000..60ae3117008 --- /dev/null +++ b/vendor/github.com/gosuri/uitable/util/wordwrap/README.md @@ -0,0 +1,39 @@ +# go-wordwrap + +`go-wordwrap` (Golang package: `wordwrap`) is a package for Go that +automatically wraps words into multiple lines. The primary use case for this +is in formatting CLI output, but of course word wrapping is a generally useful +thing to do. + +## Installation and Usage + +Install using `go get github.com/mitchellh/go-wordwrap`. + +Full documentation is available at +http://godoc.org/github.com/mitchellh/go-wordwrap + +Below is an example of its usage ignoring errors: + +```go +wrapped := wordwrap.WrapString("foo bar baz", 3) +fmt.Println(wrapped) +``` + +Would output: + +``` +foo +bar +baz +``` + +## Word Wrap Algorithm + +This library doesn't use any clever algorithm for word wrapping. The wrapping +is actually very naive: whenever there is whitespace or an explicit linebreak. +The goal of this library is for word wrapping CLI output, so the input is +typically pretty well controlled human language. Because of this, the naive +approach typically works just fine. + +In the future, we'd like to make the algorithm more advanced. We would do +so without breaking the API. diff --git a/vendor/github.com/gosuri/uitable/util/wordwrap/wordwrap.go b/vendor/github.com/gosuri/uitable/util/wordwrap/wordwrap.go new file mode 100644 index 00000000000..b2c63b87957 --- /dev/null +++ b/vendor/github.com/gosuri/uitable/util/wordwrap/wordwrap.go @@ -0,0 +1,85 @@ +// Package wordwrap provides methods for wrapping the contents of a string +package wordwrap + +import ( + "bytes" + "unicode" + + "github.com/gosuri/uitable/util/strutil" +) + +// WrapString wraps the given string within lim width in characters. +// +// Wrapping is currently naive and only happens at white-space. A future +// version of the library will implement smarter wrapping. This means that +// pathological cases can dramatically reach past the limit, such as a very +// long word. +func WrapString(s string, lim uint) string { + // Initialize a buffer with a slightly larger size to account for breaks + init := make([]byte, 0, len(s)) + buf := bytes.NewBuffer(init) + + var current uint + var wordBuf, spaceBuf bytes.Buffer + var wordWidth, spaceWidth int + + for _, char := range s { + if char == '\n' { + if wordBuf.Len() == 0 { + if current+uint(spaceWidth) > lim { + current = 0 + } else { + current += uint(spaceWidth) + spaceBuf.WriteTo(buf) + spaceWidth += strutil.StringWidth(buf.String()) + } + spaceBuf.Reset() + spaceWidth = 0 + } else { + current += uint(spaceWidth + wordWidth) + spaceBuf.WriteTo(buf) + spaceBuf.Reset() + wordBuf.WriteTo(buf) + wordBuf.Reset() + spaceWidth = 0 + wordWidth = 0 + } + buf.WriteRune(char) + current = 0 + } else if unicode.IsSpace(char) { + if spaceBuf.Len() == 0 || wordBuf.Len() > 0 { + current += uint(spaceWidth + wordWidth) + spaceBuf.WriteTo(buf) + spaceBuf.Reset() + wordBuf.WriteTo(buf) + wordBuf.Reset() + spaceWidth = 0 + wordWidth = 0 + } + + spaceBuf.WriteRune(char) + spaceWidth += strutil.RuneWidth(char) + } else { + wordBuf.WriteRune(char) + wordWidth += strutil.RuneWidth(char) + + if current+uint(spaceWidth+wordWidth) > lim && uint(wordWidth) < lim { + buf.WriteRune('\n') + current = 0 + spaceBuf.Reset() + spaceWidth = 0 + } + } + } + + if wordBuf.Len() == 0 { + if current+uint(spaceWidth) <= lim { + spaceBuf.WriteTo(buf) + } + } else { + spaceBuf.WriteTo(buf) + wordBuf.WriteTo(buf) + } + + return buf.String() +} diff --git a/vendor/github.com/mattn/go-colorable/LICENSE b/vendor/github.com/mattn/go-colorable/LICENSE new file mode 100644 index 00000000000..91b5cef30eb --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mattn/go-colorable/README.md b/vendor/github.com/mattn/go-colorable/README.md new file mode 100644 index 00000000000..ca0483711c9 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/README.md @@ -0,0 +1,48 @@ +# go-colorable + +[![Build Status](https://github.com/mattn/go-colorable/workflows/test/badge.svg)](https://github.com/mattn/go-colorable/actions?query=workflow%3Atest) +[![Codecov](https://codecov.io/gh/mattn/go-colorable/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-colorable) +[![GoDoc](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable) +[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable) + +Colorable writer for windows. + +For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) +This package is possible to handle escape sequence for ansi color on windows. + +## Too Bad! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) + + +## So Good! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) + +## Usage + +```go +logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) +logrus.SetOutput(colorable.NewColorableStdout()) + +logrus.Info("succeeded") +logrus.Warn("not correct") +logrus.Error("something error") +logrus.Fatal("panic") +``` + +You can compile above code on non-windows OSs. + +## Installation + +``` +$ go get github.com/mattn/go-colorable +``` + +# License + +MIT + +# Author + +Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-colorable/colorable_appengine.go b/vendor/github.com/mattn/go-colorable/colorable_appengine.go new file mode 100644 index 00000000000..416d1bbbf83 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_appengine.go @@ -0,0 +1,38 @@ +//go:build appengine +// +build appengine + +package colorable + +import ( + "io" + "os" + + _ "github.com/mattn/go-isatty" +) + +// NewColorable returns new instance of Writer which handles escape sequence. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + return file +} + +// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. +func NewColorableStdout() io.Writer { + return os.Stdout +} + +// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. +func NewColorableStderr() io.Writer { + return os.Stderr +} + +// EnableColorsStdout enable colors if possible. +func EnableColorsStdout(enabled *bool) func() { + if enabled != nil { + *enabled = true + } + return func() {} +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_others.go b/vendor/github.com/mattn/go-colorable/colorable_others.go new file mode 100644 index 00000000000..766d94603ac --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_others.go @@ -0,0 +1,38 @@ +//go:build !windows && !appengine +// +build !windows,!appengine + +package colorable + +import ( + "io" + "os" + + _ "github.com/mattn/go-isatty" +) + +// NewColorable returns new instance of Writer which handles escape sequence. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + return file +} + +// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. +func NewColorableStdout() io.Writer { + return os.Stdout +} + +// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. +func NewColorableStderr() io.Writer { + return os.Stderr +} + +// EnableColorsStdout enable colors if possible. +func EnableColorsStdout(enabled *bool) func() { + if enabled != nil { + *enabled = true + } + return func() {} +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go new file mode 100644 index 00000000000..1846ad5ab41 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_windows.go @@ -0,0 +1,1047 @@ +//go:build windows && !appengine +// +build windows,!appengine + +package colorable + +import ( + "bytes" + "io" + "math" + "os" + "strconv" + "strings" + "sync" + "syscall" + "unsafe" + + "github.com/mattn/go-isatty" +) + +const ( + foregroundBlue = 0x1 + foregroundGreen = 0x2 + foregroundRed = 0x4 + foregroundIntensity = 0x8 + foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) + backgroundBlue = 0x10 + backgroundGreen = 0x20 + backgroundRed = 0x40 + backgroundIntensity = 0x80 + backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) + commonLvbUnderscore = 0x8000 + + cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4 +) + +const ( + genericRead = 0x80000000 + genericWrite = 0x40000000 +) + +const ( + consoleTextmodeBuffer = 0x1 +) + +type wchar uint16 +type short int16 +type dword uint32 +type word uint16 + +type coord struct { + x short + y short +} + +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +type consoleCursorInfo struct { + size dword + visible int32 +} + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") + procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") + procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") + procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") + procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") +) + +// Writer provides colorable Writer to the console +type Writer struct { + out io.Writer + handle syscall.Handle + althandle syscall.Handle + oldattr word + oldpos coord + rest bytes.Buffer + mutex sync.Mutex +} + +// NewColorable returns new instance of Writer which handles escape sequence from File. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + if isatty.IsTerminal(file.Fd()) { + var mode uint32 + if r, _, _ := procGetConsoleMode.Call(file.Fd(), uintptr(unsafe.Pointer(&mode))); r != 0 && mode&cENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 { + return file + } + var csbi consoleScreenBufferInfo + handle := syscall.Handle(file.Fd()) + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} + } + return file +} + +// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. +func NewColorableStdout() io.Writer { + return NewColorable(os.Stdout) +} + +// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. +func NewColorableStderr() io.Writer { + return NewColorable(os.Stderr) +} + +var color256 = map[int]int{ + 0: 0x000000, + 1: 0x800000, + 2: 0x008000, + 3: 0x808000, + 4: 0x000080, + 5: 0x800080, + 6: 0x008080, + 7: 0xc0c0c0, + 8: 0x808080, + 9: 0xff0000, + 10: 0x00ff00, + 11: 0xffff00, + 12: 0x0000ff, + 13: 0xff00ff, + 14: 0x00ffff, + 15: 0xffffff, + 16: 0x000000, + 17: 0x00005f, + 18: 0x000087, + 19: 0x0000af, + 20: 0x0000d7, + 21: 0x0000ff, + 22: 0x005f00, + 23: 0x005f5f, + 24: 0x005f87, + 25: 0x005faf, + 26: 0x005fd7, + 27: 0x005fff, + 28: 0x008700, + 29: 0x00875f, + 30: 0x008787, + 31: 0x0087af, + 32: 0x0087d7, + 33: 0x0087ff, + 34: 0x00af00, + 35: 0x00af5f, + 36: 0x00af87, + 37: 0x00afaf, + 38: 0x00afd7, + 39: 0x00afff, + 40: 0x00d700, + 41: 0x00d75f, + 42: 0x00d787, + 43: 0x00d7af, + 44: 0x00d7d7, + 45: 0x00d7ff, + 46: 0x00ff00, + 47: 0x00ff5f, + 48: 0x00ff87, + 49: 0x00ffaf, + 50: 0x00ffd7, + 51: 0x00ffff, + 52: 0x5f0000, + 53: 0x5f005f, + 54: 0x5f0087, + 55: 0x5f00af, + 56: 0x5f00d7, + 57: 0x5f00ff, + 58: 0x5f5f00, + 59: 0x5f5f5f, + 60: 0x5f5f87, + 61: 0x5f5faf, + 62: 0x5f5fd7, + 63: 0x5f5fff, + 64: 0x5f8700, + 65: 0x5f875f, + 66: 0x5f8787, + 67: 0x5f87af, + 68: 0x5f87d7, + 69: 0x5f87ff, + 70: 0x5faf00, + 71: 0x5faf5f, + 72: 0x5faf87, + 73: 0x5fafaf, + 74: 0x5fafd7, + 75: 0x5fafff, + 76: 0x5fd700, + 77: 0x5fd75f, + 78: 0x5fd787, + 79: 0x5fd7af, + 80: 0x5fd7d7, + 81: 0x5fd7ff, + 82: 0x5fff00, + 83: 0x5fff5f, + 84: 0x5fff87, + 85: 0x5fffaf, + 86: 0x5fffd7, + 87: 0x5fffff, + 88: 0x870000, + 89: 0x87005f, + 90: 0x870087, + 91: 0x8700af, + 92: 0x8700d7, + 93: 0x8700ff, + 94: 0x875f00, + 95: 0x875f5f, + 96: 0x875f87, + 97: 0x875faf, + 98: 0x875fd7, + 99: 0x875fff, + 100: 0x878700, + 101: 0x87875f, + 102: 0x878787, + 103: 0x8787af, + 104: 0x8787d7, + 105: 0x8787ff, + 106: 0x87af00, + 107: 0x87af5f, + 108: 0x87af87, + 109: 0x87afaf, + 110: 0x87afd7, + 111: 0x87afff, + 112: 0x87d700, + 113: 0x87d75f, + 114: 0x87d787, + 115: 0x87d7af, + 116: 0x87d7d7, + 117: 0x87d7ff, + 118: 0x87ff00, + 119: 0x87ff5f, + 120: 0x87ff87, + 121: 0x87ffaf, + 122: 0x87ffd7, + 123: 0x87ffff, + 124: 0xaf0000, + 125: 0xaf005f, + 126: 0xaf0087, + 127: 0xaf00af, + 128: 0xaf00d7, + 129: 0xaf00ff, + 130: 0xaf5f00, + 131: 0xaf5f5f, + 132: 0xaf5f87, + 133: 0xaf5faf, + 134: 0xaf5fd7, + 135: 0xaf5fff, + 136: 0xaf8700, + 137: 0xaf875f, + 138: 0xaf8787, + 139: 0xaf87af, + 140: 0xaf87d7, + 141: 0xaf87ff, + 142: 0xafaf00, + 143: 0xafaf5f, + 144: 0xafaf87, + 145: 0xafafaf, + 146: 0xafafd7, + 147: 0xafafff, + 148: 0xafd700, + 149: 0xafd75f, + 150: 0xafd787, + 151: 0xafd7af, + 152: 0xafd7d7, + 153: 0xafd7ff, + 154: 0xafff00, + 155: 0xafff5f, + 156: 0xafff87, + 157: 0xafffaf, + 158: 0xafffd7, + 159: 0xafffff, + 160: 0xd70000, + 161: 0xd7005f, + 162: 0xd70087, + 163: 0xd700af, + 164: 0xd700d7, + 165: 0xd700ff, + 166: 0xd75f00, + 167: 0xd75f5f, + 168: 0xd75f87, + 169: 0xd75faf, + 170: 0xd75fd7, + 171: 0xd75fff, + 172: 0xd78700, + 173: 0xd7875f, + 174: 0xd78787, + 175: 0xd787af, + 176: 0xd787d7, + 177: 0xd787ff, + 178: 0xd7af00, + 179: 0xd7af5f, + 180: 0xd7af87, + 181: 0xd7afaf, + 182: 0xd7afd7, + 183: 0xd7afff, + 184: 0xd7d700, + 185: 0xd7d75f, + 186: 0xd7d787, + 187: 0xd7d7af, + 188: 0xd7d7d7, + 189: 0xd7d7ff, + 190: 0xd7ff00, + 191: 0xd7ff5f, + 192: 0xd7ff87, + 193: 0xd7ffaf, + 194: 0xd7ffd7, + 195: 0xd7ffff, + 196: 0xff0000, + 197: 0xff005f, + 198: 0xff0087, + 199: 0xff00af, + 200: 0xff00d7, + 201: 0xff00ff, + 202: 0xff5f00, + 203: 0xff5f5f, + 204: 0xff5f87, + 205: 0xff5faf, + 206: 0xff5fd7, + 207: 0xff5fff, + 208: 0xff8700, + 209: 0xff875f, + 210: 0xff8787, + 211: 0xff87af, + 212: 0xff87d7, + 213: 0xff87ff, + 214: 0xffaf00, + 215: 0xffaf5f, + 216: 0xffaf87, + 217: 0xffafaf, + 218: 0xffafd7, + 219: 0xffafff, + 220: 0xffd700, + 221: 0xffd75f, + 222: 0xffd787, + 223: 0xffd7af, + 224: 0xffd7d7, + 225: 0xffd7ff, + 226: 0xffff00, + 227: 0xffff5f, + 228: 0xffff87, + 229: 0xffffaf, + 230: 0xffffd7, + 231: 0xffffff, + 232: 0x080808, + 233: 0x121212, + 234: 0x1c1c1c, + 235: 0x262626, + 236: 0x303030, + 237: 0x3a3a3a, + 238: 0x444444, + 239: 0x4e4e4e, + 240: 0x585858, + 241: 0x626262, + 242: 0x6c6c6c, + 243: 0x767676, + 244: 0x808080, + 245: 0x8a8a8a, + 246: 0x949494, + 247: 0x9e9e9e, + 248: 0xa8a8a8, + 249: 0xb2b2b2, + 250: 0xbcbcbc, + 251: 0xc6c6c6, + 252: 0xd0d0d0, + 253: 0xdadada, + 254: 0xe4e4e4, + 255: 0xeeeeee, +} + +// `\033]0;TITLESTR\007` +func doTitleSequence(er *bytes.Reader) error { + var c byte + var err error + + c, err = er.ReadByte() + if err != nil { + return err + } + if c != '0' && c != '2' { + return nil + } + c, err = er.ReadByte() + if err != nil { + return err + } + if c != ';' { + return nil + } + title := make([]byte, 0, 80) + for { + c, err = er.ReadByte() + if err != nil { + return err + } + if c == 0x07 || c == '\n' { + break + } + title = append(title, c) + } + if len(title) > 0 { + title8, err := syscall.UTF16PtrFromString(string(title)) + if err == nil { + procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8))) + } + } + return nil +} + +// returns Atoi(s) unless s == "" in which case it returns def +func atoiWithDefault(s string, def int) (int, error) { + if s == "" { + return def, nil + } + return strconv.Atoi(s) +} + +// Write writes data on console +func (w *Writer) Write(data []byte) (n int, err error) { + w.mutex.Lock() + defer w.mutex.Unlock() + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + + handle := w.handle + + var er *bytes.Reader + if w.rest.Len() > 0 { + var rest bytes.Buffer + w.rest.WriteTo(&rest) + w.rest.Reset() + rest.Write(data) + er = bytes.NewReader(rest.Bytes()) + } else { + er = bytes.NewReader(data) + } + var plaintext bytes.Buffer +loop: + for { + c1, err := er.ReadByte() + if err != nil { + plaintext.WriteTo(w.out) + break loop + } + if c1 != 0x1b { + plaintext.WriteByte(c1) + continue + } + _, err = plaintext.WriteTo(w.out) + if err != nil { + break loop + } + c2, err := er.ReadByte() + if err != nil { + break loop + } + + switch c2 { + case '>': + continue + case ']': + w.rest.WriteByte(c1) + w.rest.WriteByte(c2) + er.WriteTo(&w.rest) + if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 { + break loop + } + er = bytes.NewReader(w.rest.Bytes()[2:]) + err := doTitleSequence(er) + if err != nil { + break loop + } + w.rest.Reset() + continue + // https://github.com/mattn/go-colorable/issues/27 + case '7': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.oldpos = csbi.cursorPosition + continue + case '8': + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) + continue + case 0x5b: + // execute part after switch + default: + continue + } + + w.rest.WriteByte(c1) + w.rest.WriteByte(c2) + er.WriteTo(&w.rest) + + var buf bytes.Buffer + var m byte + for i, c := range w.rest.Bytes()[2:] { + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + m = c + er = bytes.NewReader(w.rest.Bytes()[2+i+1:]) + w.rest.Reset() + break + } + buf.Write([]byte(string(c))) + } + if m == 0 { + break loop + } + + switch m { + case 'A': + n, err = atoiWithDefault(buf.String(), 1) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'B': + n, err = atoiWithDefault(buf.String(), 1) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'C': + n, err = atoiWithDefault(buf.String(), 1) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x += short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'D': + n, err = atoiWithDefault(buf.String(), 1) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x -= short(n) + if csbi.cursorPosition.x < 0 { + csbi.cursorPosition.x = 0 + } + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'E': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'F': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'G': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + if n < 1 { + n = 1 + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = short(n - 1) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'H', 'f': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + if buf.Len() > 0 { + token := strings.Split(buf.String(), ";") + switch len(token) { + case 1: + n1, err := strconv.Atoi(token[0]) + if err != nil { + continue + } + csbi.cursorPosition.y = short(n1 - 1) + case 2: + n1, err := strconv.Atoi(token[0]) + if err != nil { + continue + } + n2, err := strconv.Atoi(token[1]) + if err != nil { + continue + } + csbi.cursorPosition.x = short(n2 - 1) + csbi.cursorPosition.y = short(n1 - 1) + } + } else { + csbi.cursorPosition.y = 0 + } + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'J': + n := 0 + if buf.Len() > 0 { + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + } + var count, written dword + var cursor coord + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x) + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) + } + procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'K': + n := 0 + if buf.Len() > 0 { + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + var count, written dword + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + count = dword(csbi.size.x - csbi.cursorPosition.x) + case 1: + cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} + count = dword(csbi.size.x - csbi.cursorPosition.x) + case 2: + cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} + count = dword(csbi.size.x) + } + procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'X': + n := 0 + if buf.Len() > 0 { + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + var written dword + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'm': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + attr := csbi.attributes + cs := buf.String() + if cs == "" { + procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) + continue + } + token := strings.Split(cs, ";") + for i := 0; i < len(token); i++ { + ns := token[i] + if n, err = strconv.Atoi(ns); err == nil { + switch { + case n == 0 || n == 100: + attr = w.oldattr + case n == 4: + attr |= commonLvbUnderscore + case (1 <= n && n <= 3) || n == 5: + attr |= foregroundIntensity + case n == 7 || n == 27: + attr = + (attr &^ (foregroundMask | backgroundMask)) | + ((attr & foregroundMask) << 4) | + ((attr & backgroundMask) >> 4) + case n == 22: + attr &^= foregroundIntensity + case n == 24: + attr &^= commonLvbUnderscore + case 30 <= n && n <= 37: + attr &= backgroundMask + if (n-30)&1 != 0 { + attr |= foregroundRed + } + if (n-30)&2 != 0 { + attr |= foregroundGreen + } + if (n-30)&4 != 0 { + attr |= foregroundBlue + } + case n == 38: // set foreground color. + if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256foreAttr == nil { + n256setup() + } + attr &= backgroundMask + attr |= n256foreAttr[n256%len(n256foreAttr)] + i += 2 + } + } else if len(token) == 5 && token[i+1] == "2" { + var r, g, b int + r, _ = strconv.Atoi(token[i+2]) + g, _ = strconv.Atoi(token[i+3]) + b, _ = strconv.Atoi(token[i+4]) + i += 4 + if r > 127 { + attr |= foregroundRed + } + if g > 127 { + attr |= foregroundGreen + } + if b > 127 { + attr |= foregroundBlue + } + } else { + attr = attr & (w.oldattr & backgroundMask) + } + case n == 39: // reset foreground color. + attr &= backgroundMask + attr |= w.oldattr & foregroundMask + case 40 <= n && n <= 47: + attr &= foregroundMask + if (n-40)&1 != 0 { + attr |= backgroundRed + } + if (n-40)&2 != 0 { + attr |= backgroundGreen + } + if (n-40)&4 != 0 { + attr |= backgroundBlue + } + case n == 48: // set background color. + if i < len(token)-2 && token[i+1] == "5" { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256backAttr == nil { + n256setup() + } + attr &= foregroundMask + attr |= n256backAttr[n256%len(n256backAttr)] + i += 2 + } + } else if len(token) == 5 && token[i+1] == "2" { + var r, g, b int + r, _ = strconv.Atoi(token[i+2]) + g, _ = strconv.Atoi(token[i+3]) + b, _ = strconv.Atoi(token[i+4]) + i += 4 + if r > 127 { + attr |= backgroundRed + } + if g > 127 { + attr |= backgroundGreen + } + if b > 127 { + attr |= backgroundBlue + } + } else { + attr = attr & (w.oldattr & foregroundMask) + } + case n == 49: // reset foreground color. + attr &= foregroundMask + attr |= w.oldattr & backgroundMask + case 90 <= n && n <= 97: + attr = (attr & backgroundMask) + attr |= foregroundIntensity + if (n-90)&1 != 0 { + attr |= foregroundRed + } + if (n-90)&2 != 0 { + attr |= foregroundGreen + } + if (n-90)&4 != 0 { + attr |= foregroundBlue + } + case 100 <= n && n <= 107: + attr = (attr & foregroundMask) + attr |= backgroundIntensity + if (n-100)&1 != 0 { + attr |= backgroundRed + } + if (n-100)&2 != 0 { + attr |= backgroundGreen + } + if (n-100)&4 != 0 { + attr |= backgroundBlue + } + } + procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) + } + } + case 'h': + var ci consoleCursorInfo + cs := buf.String() + if cs == "5>" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 0 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?25" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 1 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?1049" { + if w.althandle == 0 { + h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0) + w.althandle = syscall.Handle(h) + if w.althandle != 0 { + handle = w.althandle + } + } + } + case 'l': + var ci consoleCursorInfo + cs := buf.String() + if cs == "5>" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 1 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?25" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 0 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?1049" { + if w.althandle != 0 { + syscall.CloseHandle(w.althandle) + w.althandle = 0 + handle = w.handle + } + } + case 's': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.oldpos = csbi.cursorPosition + case 'u': + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) + } + } + + return len(data), nil +} + +type consoleColor struct { + rgb int + red bool + green bool + blue bool + intensity bool +} + +func (c consoleColor) foregroundAttr() (attr word) { + if c.red { + attr |= foregroundRed + } + if c.green { + attr |= foregroundGreen + } + if c.blue { + attr |= foregroundBlue + } + if c.intensity { + attr |= foregroundIntensity + } + return +} + +func (c consoleColor) backgroundAttr() (attr word) { + if c.red { + attr |= backgroundRed + } + if c.green { + attr |= backgroundGreen + } + if c.blue { + attr |= backgroundBlue + } + if c.intensity { + attr |= backgroundIntensity + } + return +} + +var color16 = []consoleColor{ + {0x000000, false, false, false, false}, + {0x000080, false, false, true, false}, + {0x008000, false, true, false, false}, + {0x008080, false, true, true, false}, + {0x800000, true, false, false, false}, + {0x800080, true, false, true, false}, + {0x808000, true, true, false, false}, + {0xc0c0c0, true, true, true, false}, + {0x808080, false, false, false, true}, + {0x0000ff, false, false, true, true}, + {0x00ff00, false, true, false, true}, + {0x00ffff, false, true, true, true}, + {0xff0000, true, false, false, true}, + {0xff00ff, true, false, true, true}, + {0xffff00, true, true, false, true}, + {0xffffff, true, true, true, true}, +} + +type hsv struct { + h, s, v float32 +} + +func (a hsv) dist(b hsv) float32 { + dh := a.h - b.h + switch { + case dh > 0.5: + dh = 1 - dh + case dh < -0.5: + dh = -1 - dh + } + ds := a.s - b.s + dv := a.v - b.v + return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) +} + +func toHSV(rgb int) hsv { + r, g, b := float32((rgb&0xFF0000)>>16)/256.0, + float32((rgb&0x00FF00)>>8)/256.0, + float32(rgb&0x0000FF)/256.0 + min, max := minmax3f(r, g, b) + h := max - min + if h > 0 { + if max == r { + h = (g - b) / h + if h < 0 { + h += 6 + } + } else if max == g { + h = 2 + (b-r)/h + } else { + h = 4 + (r-g)/h + } + } + h /= 6.0 + s := max - min + if max != 0 { + s /= max + } + v := max + return hsv{h: h, s: s, v: v} +} + +type hsvTable []hsv + +func toHSVTable(rgbTable []consoleColor) hsvTable { + t := make(hsvTable, len(rgbTable)) + for i, c := range rgbTable { + t[i] = toHSV(c.rgb) + } + return t +} + +func (t hsvTable) find(rgb int) consoleColor { + hsv := toHSV(rgb) + n := 7 + l := float32(5.0) + for i, p := range t { + d := hsv.dist(p) + if d < l { + l, n = d, i + } + } + return color16[n] +} + +func minmax3f(a, b, c float32) (min, max float32) { + if a < b { + if b < c { + return a, c + } else if a < c { + return a, b + } else { + return c, b + } + } else { + if a < c { + return b, c + } else if b < c { + return b, a + } else { + return c, a + } + } +} + +var n256foreAttr []word +var n256backAttr []word + +func n256setup() { + n256foreAttr = make([]word, 256) + n256backAttr = make([]word, 256) + t := toHSVTable(color16) + for i, rgb := range color256 { + c := t.find(rgb) + n256foreAttr[i] = c.foregroundAttr() + n256backAttr[i] = c.backgroundAttr() + } +} + +// EnableColorsStdout enable colors if possible. +func EnableColorsStdout(enabled *bool) func() { + var mode uint32 + h := os.Stdout.Fd() + if r, _, _ := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode))); r != 0 { + if r, _, _ = procSetConsoleMode.Call(h, uintptr(mode|cENABLE_VIRTUAL_TERMINAL_PROCESSING)); r != 0 { + if enabled != nil { + *enabled = true + } + return func() { + procSetConsoleMode.Call(h, uintptr(mode)) + } + } + } + if enabled != nil { + *enabled = true + } + return func() {} +} diff --git a/vendor/github.com/mattn/go-colorable/go.test.sh b/vendor/github.com/mattn/go-colorable/go.test.sh new file mode 100644 index 00000000000..012162b077c --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/go.test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt + +for d in $(go list ./... | grep -v vendor); do + go test -race -coverprofile=profile.out -covermode=atomic "$d" + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done diff --git a/vendor/github.com/mattn/go-colorable/noncolorable.go b/vendor/github.com/mattn/go-colorable/noncolorable.go new file mode 100644 index 00000000000..05d6f74bf6b --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/noncolorable.go @@ -0,0 +1,57 @@ +package colorable + +import ( + "bytes" + "io" +) + +// NonColorable holds writer but removes escape sequence. +type NonColorable struct { + out io.Writer +} + +// NewNonColorable returns new instance of Writer which removes escape sequence from Writer. +func NewNonColorable(w io.Writer) io.Writer { + return &NonColorable{out: w} +} + +// Write writes data on console +func (w *NonColorable) Write(data []byte) (n int, err error) { + er := bytes.NewReader(data) + var plaintext bytes.Buffer +loop: + for { + c1, err := er.ReadByte() + if err != nil { + plaintext.WriteTo(w.out) + break loop + } + if c1 != 0x1b { + plaintext.WriteByte(c1) + continue + } + _, err = plaintext.WriteTo(w.out) + if err != nil { + break loop + } + c2, err := er.ReadByte() + if err != nil { + break loop + } + if c2 != 0x5b { + continue + } + + for { + c, err := er.ReadByte() + if err != nil { + break loop + } + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + break + } + } + } + + return len(data), nil +} diff --git a/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/mattn/go-isatty/LICENSE new file mode 100644 index 00000000000..65dc692b6b1 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) Yasuhiro MATSUMOTO + +MIT License (Expat) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md new file mode 100644 index 00000000000..38418353e31 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/README.md @@ -0,0 +1,50 @@ +# go-isatty + +[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty) +[![Codecov](https://codecov.io/gh/mattn/go-isatty/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-isatty) +[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) +[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty) + +isatty for golang + +## Usage + +```go +package main + +import ( + "fmt" + "github.com/mattn/go-isatty" + "os" +) + +func main() { + if isatty.IsTerminal(os.Stdout.Fd()) { + fmt.Println("Is Terminal") + } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { + fmt.Println("Is Cygwin/MSYS2 Terminal") + } else { + fmt.Println("Is Not Terminal") + } +} +``` + +## Installation + +``` +$ go get github.com/mattn/go-isatty +``` + +## License + +MIT + +## Author + +Yasuhiro Matsumoto (a.k.a mattn) + +## Thanks + +* k-takata: base idea for IsCygwinTerminal + + https://github.com/k-takata/go-iscygpty diff --git a/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/mattn/go-isatty/doc.go new file mode 100644 index 00000000000..17d4f90ebcc --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/doc.go @@ -0,0 +1,2 @@ +// Package isatty implements interface to isatty +package isatty diff --git a/vendor/github.com/mattn/go-isatty/go.test.sh b/vendor/github.com/mattn/go-isatty/go.test.sh new file mode 100644 index 00000000000..012162b077c --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/go.test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt + +for d in $(go list ./... | grep -v vendor); do + go test -race -coverprofile=profile.out -covermode=atomic "$d" + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go new file mode 100644 index 00000000000..d569c0c9499 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -0,0 +1,19 @@ +//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine +// +build darwin freebsd openbsd netbsd dragonfly hurd +// +build !appengine + +package isatty + +import "golang.org/x/sys/unix" + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + _, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go new file mode 100644 index 00000000000..31503226f6c --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -0,0 +1,16 @@ +//go:build appengine || js || nacl || wasm +// +build appengine js nacl wasm + +package isatty + +// IsTerminal returns true if the file descriptor is terminal which +// is always false on js and appengine classic which is a sandboxed PaaS. +func IsTerminal(fd uintptr) bool { + return false +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_plan9.go b/vendor/github.com/mattn/go-isatty/isatty_plan9.go new file mode 100644 index 00000000000..bae7f9bb3dc --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_plan9.go @@ -0,0 +1,23 @@ +//go:build plan9 +// +build plan9 + +package isatty + +import ( + "syscall" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd uintptr) bool { + path, err := syscall.Fd2path(int(fd)) + if err != nil { + return false + } + return path == "/dev/cons" || path == "/mnt/term/dev/cons" +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go new file mode 100644 index 00000000000..0c3acf2dc28 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_solaris.go @@ -0,0 +1,21 @@ +//go:build solaris && !appengine +// +build solaris,!appengine + +package isatty + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +// see: https://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/isatty.c +func IsTerminal(fd uintptr) bool { + _, err := unix.IoctlGetTermio(int(fd), unix.TCGETA) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go new file mode 100644 index 00000000000..67787657fb2 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go @@ -0,0 +1,19 @@ +//go:build (linux || aix || zos) && !appengine +// +build linux aix zos +// +build !appengine + +package isatty + +import "golang.org/x/sys/unix" + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go new file mode 100644 index 00000000000..8e3c99171bf --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go @@ -0,0 +1,125 @@ +//go:build windows && !appengine +// +build windows,!appengine + +package isatty + +import ( + "errors" + "strings" + "syscall" + "unicode/utf16" + "unsafe" +) + +const ( + objectNameInfo uintptr = 1 + fileNameInfo = 2 + fileTypePipe = 3 +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + ntdll = syscall.NewLazyDLL("ntdll.dll") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") + procGetFileType = kernel32.NewProc("GetFileType") + procNtQueryObject = ntdll.NewProc("NtQueryObject") +) + +func init() { + // Check if GetFileInformationByHandleEx is available. + if procGetFileInformationByHandleEx.Find() != nil { + procGetFileInformationByHandleEx = nil + } +} + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// Check pipe name is used for cygwin/msys2 pty. +// Cygwin/MSYS2 PTY has a name like: +// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master +func isCygwinPipeName(name string) bool { + token := strings.Split(name, "-") + if len(token) < 5 { + return false + } + + if token[0] != `\msys` && + token[0] != `\cygwin` && + token[0] != `\Device\NamedPipe\msys` && + token[0] != `\Device\NamedPipe\cygwin` { + return false + } + + if token[1] == "" { + return false + } + + if !strings.HasPrefix(token[2], "pty") { + return false + } + + if token[3] != `from` && token[3] != `to` { + return false + } + + if token[4] != "master" { + return false + } + + return true +} + +// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler +// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion +// guys are using Windows XP, this is a workaround for those guys, it will also work on system from +// Windows vista to 10 +// see https://stackoverflow.com/a/18792477 for details +func getFileNameByHandle(fd uintptr) (string, error) { + if procNtQueryObject == nil { + return "", errors.New("ntdll.dll: NtQueryObject not supported") + } + + var buf [4 + syscall.MAX_PATH]uint16 + var result int + r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5, + fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0) + if r != 0 { + return "", e + } + return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. +func IsCygwinTerminal(fd uintptr) bool { + if procGetFileInformationByHandleEx == nil { + name, err := getFileNameByHandle(fd) + if err != nil { + return false + } + return isCygwinPipeName(name) + } + + // Cygwin/msys's pty is a pipe. + ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) + if ft != fileTypePipe || e != 0 { + return false + } + + var buf [2 + syscall.MAX_PATH]uint16 + r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), + 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), + uintptr(len(buf)*2), 0, 0) + if r == 0 || e != 0 { + return false + } + + l := *(*uint32)(unsafe.Pointer(&buf)) + return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 32ae77cecb1..80324c7f55a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -423,6 +423,9 @@ github.com/emicklei/go-restful/v3/log # github.com/evanphx/json-patch v4.12.0+incompatible ## explicit github.com/evanphx/json-patch +# github.com/fatih/color v1.13.0 +## explicit; go 1.13 +github.com/fatih/color # github.com/fsnotify/fsnotify v1.5.4 ## explicit; go 1.16 github.com/fsnotify/fsnotify @@ -510,6 +513,11 @@ github.com/google/uuid # github.com/gorilla/mux v1.8.0 ## explicit; go 1.12 github.com/gorilla/mux +# github.com/gosuri/uitable v0.0.4 +## explicit +github.com/gosuri/uitable +github.com/gosuri/uitable/util/strutil +github.com/gosuri/uitable/util/wordwrap # github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 ## explicit github.com/gregjones/httpcache @@ -617,6 +625,12 @@ github.com/mailru/easyjson/jwriter github.com/manifoldco/promptui github.com/manifoldco/promptui/list github.com/manifoldco/promptui/screenbuf +# github.com/mattn/go-colorable v0.1.12 +## explicit; go 1.13 +github.com/mattn/go-colorable +# github.com/mattn/go-isatty v0.0.17 +## explicit; go 1.15 +github.com/mattn/go-isatty # github.com/mattn/go-runewidth v0.0.14 ## explicit; go 1.9 github.com/mattn/go-runewidth diff --git a/version/types.go b/version/types.go deleted file mode 100644 index 8a83cfdcff3..00000000000 --- a/version/types.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2021 Alibaba Group Holding Ltd. -// -// 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 version - -// Info contains versioning information. -// TODO: Add []string of api versions supported? It's still unclear -// how we'll want to distribute that information. -type Info struct { - Major string `json:"major,omitempty"` - Minor string `json:"minor,omitempty"` - GitVersion string `json:"gitVersion"` - GitCommit string `json:"gitCommit,omitempty"` - BuildDate string `json:"buildDate"` - GoVersion string `json:"goVersion"` - Compiler string `json:"compiler"` - Platform string `json:"platform"` -} - -// String returns info as a human-friendly version string. -func (info Info) String() string { - return info.GitVersion -}