From b162f46d93e395d06c0e714fd19b682d3cfd18b6 Mon Sep 17 00:00:00 2001 From: Mehdi BEN ABDALLAH Date: Thu, 22 Feb 2018 18:40:41 +0100 Subject: [PATCH] first commit --- .gitignore | 1 + Makefile | 78 +++++++++++++++++++++++ README.md | 56 ++++++++++++++++- glide.lock | 47 ++++++++++++++ glide.yaml | 10 +++ install-binary.sh | 151 +++++++++++++++++++++++++++++++++++++++++++++ main.go | 153 ++++++++++++++++++++++++++++++++++++++++++++++ plugin.yaml | 10 +++ 8 files changed, 504 insertions(+), 2 deletions(-) create mode 100644 Makefile create mode 100644 glide.lock create mode 100644 glide.yaml create mode 100755 install-binary.sh create mode 100644 main.go create mode 100644 plugin.yaml diff --git a/.gitignore b/.gitignore index a1338d6..b4529ff 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ +vendor/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..12da706 --- /dev/null +++ b/Makefile @@ -0,0 +1,78 @@ +PROJECT_BIN_NAME=local-chart-version +PROJECT_NAME=helm-$(PROJECT_BIN_NAME) +PROJECT_GH=mbenabda/$(PROJECT_NAME) +PKG:= github.com/$(PROJECT_GH) + +HELM_HOME ?= $(shell helm home) +VERSION := $(shell sed -n -e 's/version:[ "]*\([^"]*\).*/\1/p' plugin.yaml) +TARGETS ?= darwin/amd64 linux/amd64 linux/386 windows/amd64 + +LDFLAGS := -X main.Version=$(VERSION) +# Clear the "unreleased" string in BuildMetadata +LDFLAGS += -X $(PKG)/vendor/k8s.io/helm/pkg/version.BuildMetadata= +LDFLAGS += -X $(PKG)/vendor/k8s.io/helm/pkg/version.Version=$(shell grep -A1 "package: k8s.io/helm" glide.yaml | sed -n -e 's/[ ]*version:.*\(v[.0-9]*\).*/\1/p') + +.PHONY: clean +clean: + @rm -rf $(PROJECT_BIN_NAME) ./build ./dist + +.PHONY: build-cross +build-cross: LDFLAGS += -extldflags "-static" +build-cross: + CGO_ENABLED=0 gox \ + -verbose \ + -parallel=3 \ + -output="dist/{{.OS}}-{{.Arch}}/{{.Dir}}" \ + -ldflags "$(LDFLAGS)" \ + -osarch="$(TARGETS)" \ + $(PKG) + +.PHONY: dist +dist: export COPYFILE_DISABLE=1 #teach OSX tar to not put ._* files in tar archive +dist: + ( \ + cd dist && \ + find * -type d -exec cp ../README.md {} \; && \ + find * -type d -exec cp ../plugin.yaml {} \; && \ + find * -type d -exec tar -zcf ${PROJECT_BIN_NAME}-${VERSION}-{}.tgz {} \; \ + ) + +HAS_GLIDE := $(shell command -v glide;) +HAS_GOX := $(shell command -v gox;) +HAS_GIT := $(shell command -v git;) +HAS_GITHUB_RELEASE := $(shell command -v github-release;) + +.PHONY: bootstrap +bootstrap: +ifndef HAS_GLIDE + go get -u github.com/Masterminds/glide +endif +ifndef HAS_GOX + go get -u github.com/mitchellh/gox +endif +ifndef HAS_GIT + $(error You must install Git) +endif +ifndef HAS_GITHUB_RELEASE + go get -u github.com/c4milo/github-release +endif + glide install --strip-vendor + +.PHONY: build +build: + go build -i -v -o ${PROJECT_BIN_NAME} -ldflags="$(LDFLAGS)" + +.PHONY: install +install: bootstrap build + mkdir -p $(HELM_HOME)/plugins/${PROJECT_NAME} + cp ${PROJECT_BIN_NAME} $(HELM_HOME)/plugins/${PROJECT_NAME}/ + cp plugin.yaml $(HELM_HOME)/plugins/${PROJECT_NAME}/ + +# usage: make clean bootstrap build-cross dist release +.PHONY: release +release: dist +ifndef GITHUB_TOKEN + $(error GITHUB_TOKEN is undefined) +endif + git push + github-release ${PROJECT_GH} v$(VERSION) master "v$(VERSION)" "dist/*.*" diff --git a/README.md b/README.md index fac1baf..95c5971 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,54 @@ -# helm-chart-version -Bump Helm Chart version on local filesystem (golang port of github.com/shaneramey/helm-local-bump) +# Helm Chart Version Plugin + +This is a Helm plugin that helps you change your chart version. +It is meant to be used in integration pipelines. + +## Usage + +``` +helm local-chart-version [subcommand] [PATH_TO_LOCAL_CHART] [flags] +``` + +know more: +``` +helm local-chart-version help +``` + +## Install + +### Using Helm plugin manager (> 2.3.x) + +```shell +helm plugin install https://github.com/mbenabda/helm-local-chart-version +``` + +### Pre Helm 2.3.0 Installation +Pick a release tarball from the [releases](https://github.com/mbenabda/helm-local-chart-version/releases) page. + +Unpack the tarball in your helm plugins directory (`$(helm home)/plugins`). + +E.g. +``` +curl -L $TARBALL_URL | tar -C $(helm home)/plugins -xzv +``` + +## Build + +Clone the repository into your `$GOPATH` and then build it. + +``` +$ mkdir -p $GOPATH/src/github.com/mbenabda/ +$ cd $GOPATH/src/github.com/mbenabda/ +$ git clone https://github.com/mbenabda/helm-local-chart-version.git +$ cd helm-local-chart-version +$ make install +``` + +The above will install this plugin into your `$HELM_HOME/plugins` directory. + +### Prerequisites + +- You need to have [Go](http://golang.org) installed. Make sure to set `$GOPATH` +- If you don't have [Glide](http://glide.sh) installed, this will install it into + `$GOPATH/bin` for you. +- install [github.com/c4milo/github-release](https://github.com/c4milo/github-release) \ No newline at end of file diff --git a/glide.lock b/glide.lock new file mode 100644 index 0000000..0d82dd2 --- /dev/null +++ b/glide.lock @@ -0,0 +1,47 @@ +hash: 2537e920a3d03a922fd6f75baa1b7e11b2a66fd84718e4efff51cb2f2ed16b95 +updated: 2018-02-22T23:56:36.259825774+01:00 +imports: +- name: github.com/BurntSushi/toml + version: b26d9c308763d68093482582cea63d69be07a0f0 +- name: github.com/ghodss/yaml + version: 0ca9ea5df5451ffdf184b4428c902747c2c11cd7 +- name: github.com/gobwas/glob + version: 5ccd90ef52e1e632236f7326478d4faa74f99438 + subpackages: + - compiler + - match + - syntax + - syntax/ast + - syntax/lexer + - util/runes + - util/strings +- name: github.com/golang/protobuf + version: 4bd1920723d7b7c925de087aa32e2187708897f7 + subpackages: + - proto + - ptypes/any + - ptypes/timestamp +- name: github.com/inconshreveable/mousetrap + version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +- name: github.com/Masterminds/semver + version: 517734cc7d6470c0d07130e40fd40bdeb9bcd3fd +- name: github.com/spf13/cobra + version: 7b2c5ac9fc04fc5efafb60700713d4fa609b777b +- name: github.com/spf13/pflag + version: ee5fd03fd6acfd43e44aea0b4135958546ed8e73 +- name: gopkg.in/yaml.v2 + version: d670f9405373e636a5a2765eea47fac0c9bc91a4 +- name: k8s.io/apimachinery + version: cced8e64b6ca92a8b6afcbfea3353ca016694a45 + subpackages: + - pkg/version +- name: k8s.io/helm + version: 6af75a8fd72e2aa18a2b278cfe5c7a1c5feca7f2 + subpackages: + - pkg/chartutil + - pkg/ignore + - pkg/proto/hapi/chart + - pkg/proto/hapi/version + - pkg/sympath + - pkg/version +testImports: [] diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..f0ac4b0 --- /dev/null +++ b/glide.yaml @@ -0,0 +1,10 @@ +package: github.com/mbenabda/helm-local-chart-version +import: +- package: github.com/Masterminds/semver + version: ~1.3.1 +- package: github.com/spf13/cobra + version: ~0.0.1 +- package: k8s.io/helm + version: ~2.8.1 + subpackages: + - pkg/chartutil diff --git a/install-binary.sh b/install-binary.sh new file mode 100755 index 0000000..b388bf1 --- /dev/null +++ b/install-binary.sh @@ -0,0 +1,151 @@ +#!/bin/bash + +# Shamelessly copied from https://github.com/technosophos/helm-template +# +# Helm Template Plugin +# Copyright (C) 2016, Matt Butcher +# +# 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. + +PROJECT_BIN_NAME="local-chart-version" +PROJECT_NAME="helm-${PROJECT_BIN_NAME}" +PROJECT_GH="mbenabda/${PROJECT_NAME}" + +: ${HELM_PLUGIN_PATH:="$(helm home)/plugins/${PROJECT_NAME}"} + +# Convert the HELM_PLUGIN_PATH to unix if cygpath is +# available. This is the case when using MSYS2 or Cygwin +# on Windows where helm returns a Windows path but we +# need a Unix path + +if type cygpath > /dev/null 2>&1; then + HELM_PLUGIN_PATH=$(cygpath -u $HELM_PLUGIN_PATH) +fi + +if [[ $SKIP_BIN_INSTALL == "1" ]]; then + echo "Skipping binary install" + exit +fi + +# initArch discovers the architecture for this system. +initArch() { + ARCH=$(uname -m) + case $ARCH in + x86) ARCH="386";; + x86_64) ARCH="amd64";; + i686) ARCH="386";; + i386) ARCH="386";; + esac +} + +# initOS discovers the operating system for this system. +initOS() { + OS=$(echo `uname`|tr '[:upper:]' '[:lower:]') + + case "$OS" in + # Msys support + msys*) OS='windows';; + # Minimalist GNU for Windows + mingw*) OS='windows';; + esac +} + +# verifySupported checks that the os/arch combination is supported for +# binary builds. +verifySupported() { + local supported="darwin-amd64\nlinux-amd64\nlinux-386\nwindows-amd64" + if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then + echo "No prebuilt binary for ${OS}-${ARCH}." + exit 1 + fi + + if ! type "curl" > /dev/null && ! type "wget" > /dev/null; then + echo "Either curl or wget is required" + exit 1 + fi +} + +# getDownloadURL checks the latest available version. +getDownloadURL() { + local url="https://api.github.com/repos/$PROJECT_GH/releases/latest" + local version=$(git describe --tags --exact-match 2>/dev/null) + if [ -n "$version" ]; then + url="https://api.github.com/repos/$PROJECT_GH/releases/tags/$version" + fi + # Use the GitHub API to find the download url for this project. + if type "curl" > /dev/null; then + DOWNLOAD_URL=$(curl -v -s $url | grep $OS | awk '/\"browser_download_url\":/{gsub( /[,\"]/,"", $2); print $2}') + elif type "wget" > /dev/null; then + DOWNLOAD_URL=$(wget -q -O - $url | grep $OS | awk '/\"browser_download_url\":/{gsub( /[,\"]/,"", $2); print $2}') + fi +} + +# downloadFile downloads the latest binary package and also the checksum +# for that binary. +downloadFile() { + PLUGIN_TMP_FILE="/tmp/${PROJECT_NAME}.tgz" + echo "Downloading $DOWNLOAD_URL" + if type "curl" > /dev/null; then + curl -L "$DOWNLOAD_URL" -o "$PLUGIN_TMP_FILE" + elif type "wget" > /dev/null; then + wget -q -O "$PLUGIN_TMP_FILE" "$DOWNLOAD_URL" + fi +} + +# installFile verifies the SHA256 for the file, then unpacks and +# installs it. +installFile() { + HELM_TMP="/tmp/$PROJECT_NAME" + mkdir -p "$HELM_TMP" + tar xf "$PLUGIN_TMP_FILE" -C "$HELM_TMP" + HELM_TMP_BIN="$HELM_TMP/${PROJECT_NAME}/${PROJECT_BIN_NAME}" + echo "Preparing to install into ${HELM_PLUGIN_PATH}" + cp "$HELM_TMP_BIN" "$HELM_PLUGIN_PATH" +} + +# fail_trap is executed if an error occurs. +fail_trap() { + result=$? + if [ "$result" != "0" ]; then + echo "Failed to install $PROJECT_NAME" + echo "\tFor support, go to https://github.com/${PROJECT_GH}." + fi + exit $result +} + +# testVersion tests the installed client to make sure it is working. +testVersion() { + set +e + echo "$PROJECT_NAME installed into $HELM_PLUGIN_PATH/$PROJECT_BIN_NAME" + $HELM_PLUGIN_PATH/$PROJECT_BIN_NAME -h + set -e +} + +# Execution + +#Stop execution on any error +trap "fail_trap" EXIT +set -e +initArch +initOS +verifySupported +getDownloadURL +downloadFile +installFile +testVersion diff --git a/main.go b/main.go new file mode 100644 index 0000000..bdad7b7 --- /dev/null +++ b/main.go @@ -0,0 +1,153 @@ +package main + +import ( + "io" + "path/filepath" + + "k8s.io/helm/pkg/proto/hapi/chart" + + "github.com/spf13/cobra" + "k8s.io/helm/pkg/chartutil" + + "fmt" + "os" + + "github.com/Masterminds/semver" +) + +// Version identifier populated via the CI/CD process. +var Version = "HEAD" + +type setVersionCommand struct { + chart string + version string + out io.Writer +} + +type bumpVersionCommand struct { + chart string + segment string + out io.Writer +} + +func (c *setVersionCommand) run() error { + chart, err := chartutil.Load(c.chart) + if err != nil { + return err + } + + chart.Metadata.Version = c.version + + return writeChartFile(chart, c.chart) +} + +func writeChartFile(c *chart.Chart, dest string) error { + return chartutil.SaveChartfile(filepath.Join(dest, "Chart.yaml"), c.Metadata) +} + +func incrementVersion(version string, segment string) (string, error) { + v1, err := semver.NewVersion(version) + if err != nil { + return "", err + } + + var v2 semver.Version + switch segment { + case "patch": + v2 = v1.IncPatch() + case "minor": + v2 = v1.IncMinor() + case "major": + v2 = v1.IncMajor() + default: + return "", fmt.Errorf("Unknown version segment %s", segment) + } + + return v2.String(), nil +} + +func (c *bumpVersionCommand) run() error { + chart, err := chartutil.Load(c.chart) + if err != nil { + return err + } + + incrementedVersion, err := incrementVersion(chart.Metadata.Version, c.segment) + if err != nil { + return err + } + + chart.Metadata.Version = incrementedVersion + + return writeChartFile(chart, c.chart) +} + +func newSetVersionCommand(out io.Writer) *cobra.Command { + sc := &setVersionCommand{out: out} + + cmd := &cobra.Command{ + Use: "set --chart [PATH_TO_CHART_DIRECTORY] --version [version]", + Short: "Modify a local chart's version number in place", + RunE: func(cmd *cobra.Command, args []string) error { + return sc.run() + }, + } + + f := cmd.Flags() + f.StringVarP(&sc.chart, "chart", "c", "", "Path to a local chart's root directory") + f.StringVarP(&sc.version, "version", "v", "", "New version of the chart") + + cmd.MarkFlagRequired("chart") + cmd.MarkFlagRequired("version") + + return cmd +} + +func newBumpVersionCommand(out io.Writer) *cobra.Command { + bc := &bumpVersionCommand{out: out} + + cmd := &cobra.Command{ + Use: "bump --chart [PATH_TO_CHART_DIRECTORY] --version-segment (major|minor|patch)", + Short: "Increment the desired segment of a local chart's version", + RunE: func(cmd *cobra.Command, args []string) error { + return bc.run() + }, + } + + f := cmd.Flags() + f.StringVarP(&bc.chart, "chart", "c", "", "Path to a local chart's root directory") + f.StringVarP(&bc.segment, "version-segment", "s", "", "segment of the chart's version to bump (major|minor|patch)") + + cmd.MarkFlagRequired("chart") + cmd.MarkFlagRequired("version-segment") + + return cmd +} + +func main() { + rootCmd := &cobra.Command{ + Use: "local-chart-version", + Long: "Modify the version number of a local helm chart", + } + + out := rootCmd.OutOrStdout() + + fmt.Fprintln(out, "Helm local-chart-version Plugin --", Version) + fmt.Fprintln(out, "") + + rootCmd.AddCommand(&cobra.Command{ + Use: "version", + Short: "Print the version of the local-chart-version helm plugin", + Long: "All software has versions. This is helm-local-chart-version's", + Run: func(cmd *cobra.Command, args []string) { + fmt.Fprintln(out, "Helm local-chart-version Plugin --", Version) + }, + }) + + rootCmd.AddCommand(newSetVersionCommand(out)) + rootCmd.AddCommand(newBumpVersionCommand(out)) + + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/plugin.yaml b/plugin.yaml new file mode 100644 index 0000000..ae6ef1e --- /dev/null +++ b/plugin.yaml @@ -0,0 +1,10 @@ +name: "local-chart-version" +version: "0.0.1" +usage: "local-chart-version [subcommand] LOCAL_CHART_DIRECTORY [flags]" +description: |- + Helm plugin for bumping/manually setting the version number of a local helm chart + Usage: "local-chart-version [subcommand] LOCAL_CHART_DIRECTORY [flags]" +command: "$HELM_PLUGIN_DIR/local-chart-version" +hooks: + install: "$HELM_PLUGIN_DIR/install-binary.sh" +ignoreFlags: false