Repository: kubernetes/kompose
Pull request #1524 (2022-10-16 22:08:27)

Fix environment variables interpolation
# Description
This pull request aims to solve #1497.
In the previous implementation, we were loading the `docker-compose` file while activating variables interpolation, because we are using the default options of the Docker CLI Loader.

# Solution
To mitigate this problem, we need to disable the default interpolation, and then adapt the env variable to the K8S style. e.g: `$PROTOCOL` => `$(PROTOCOL)`

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/kompose/pull/1524#" title="Author self-approved">AhmedGrati</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign ngtuna for approval by writing `/assign @ngtuna` in a comment. For more information see:[The Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Fkompose).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes/kompose/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["ngtuna"]} -->
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/kompose/pull/1524#" title="Author self-approved">AhmedGrati</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign ngtuna for approval by writing `/assign @ngtuna` in a comment. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Fkompose).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes/kompose/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["ngtuna"]} -->
k8s-ci-robot: @AhmedGrati: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

Patch:
diff --git a/README.md b/README.md
index a7df70ad..5e53dc51 100644
--- a/README.md
+++ b/README.md
@@ -48,10 +48,10 @@ __Linux and macOS:__
 
 ```sh
 # Linux
-curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.1/kompose-linux-amd64 -o kompose
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.27.0/kompose-linux-amd64 -o kompose
 
 # macOS
-curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.1/kompose-darwin-amd64 -o kompose
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.27.0/kompose-darwin-amd64 -o kompose
 
 chmod +x kompose
 sudo mv ./kompose /usr/local/bin/kompose
@@ -59,7 +59,7 @@ sudo mv ./kompose /usr/local/bin/kompose
 
 __Windows:__
 
-Download from [GitHub](https://github.com/kubernetes/kompose/releases/download/v1.26.1/kompose-windows-amd64.exe) and add the binary to your PATH.
+Download from [GitHub](https://github.com/kubernetes/kompose/releases/download/v1.27.0/kompose-windows-amd64.exe) and add the binary to your PATH.
 
 ## Shell autocompletion
 
diff --git a/build/VERSION b/build/VERSION
index dd43a143..5db08bf2 100644
--- a/build/VERSION
+++ b/build/VERSION
@@ -1,1 +1,1 @@
-1.26.1
+1.27.0
diff --git a/docs/installation.md b/docs/installation.md
index 2b25f329..17f43308 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -22,10 +22,10 @@ __Linux and macOS:__
 
 ```sh
 # Linux
-curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.1/kompose-linux-amd64 -o kompose
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.27.0/kompose-linux-amd64 -o kompose
 
 # macOS
-curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.1/kompose-darwin-amd64 -o kompose
+curl -L https://github.com/kubernetes/kompose/releases/download/v1.27.0/kompose-darwin-amd64 -o kompose
 
 chmod +x kompose
 sudo mv ./kompose /usr/local/bin/kompose
@@ -33,7 +33,7 @@ sudo mv ./kompose /usr/local/bin/kompose
 
 __Windows:__
 
-Download from [GitHub](https://github.com/kubernetes/kompose/releases/download/v1.26.1/kompose-windows-amd64.exe) and add the binary to your PATH.
+Download from [GitHub](https://github.com/kubernetes/kompose/releases/download/v1.27.0/kompose-windows-amd64.exe) and add the binary to your PATH.
 
 ## Go
 
@@ -66,8 +66,8 @@ sudo dnf -y install kompose
 A deb package is released for compose. Download latest package in the assets in [github releases](https://github.com/kubernetes/kompose/releases).
 
 ```bash
-wget https://github.com/kubernetes/kompose/releases/download/v1.26.1/kompose_1.26.1_amd64.deb # Replace 1.26.1 with latest tag
-sudo apt install ./kompose_1.26.1_amd64.deb
+wget https://github.com/kubernetes/kompose/releases/download/v1.27.0/kompose_1.27.0_amd64.deb # Replace 1.27.0 with latest tag
+sudo apt install ./kompose_1.27.0_amd64.deb
 ```
 
 ## macOS
diff --git a/pkg/kobject/kobject.go b/pkg/kobject/kobject.go
index c5913d6c..9a85fd1c 100644
--- a/pkg/kobject/kobject.go
+++ b/pkg/kobject/kobject.go
@@ -91,6 +91,7 @@ func (opt *ConvertOptions) IsPodController() bool {
 	return opt.IsDeploymentFlag || opt.IsDaemonSetFlag || opt.IsReplicationControllerFlag || opt.Controller != ""
 }
 
+// ServiceConfigGroup holds an array of a ServiceConfig objects.
 type ServiceConfigGroup []ServiceConfig
 
 // ServiceConfig holds the basic struct of a container
diff --git a/pkg/loader/compose/v3.go b/pkg/loader/compose/v3.go
index 2e218e76..1bd7399b 100644
--- a/pkg/loader/compose/v3.go
+++ b/pkg/loader/compose/v3.go
@@ -105,7 +105,9 @@ func parseV3(files []string) (kobject.KomposeObject, error) {
 		// We load it in order to retrieve the parsed output configuration!
 		// This will output a github.com/docker/cli ServiceConfig
 		// Which is similar to our version of ServiceConfig
-		currentConfig, err := loader.Load(configDetails)
+		currentConfig, err := loader.Load(configDetails, func(options *loader.Options) {
+			options.SkipInterpolation = true
+		})
 		if err != nil {
 			return kobject.KomposeObject{}, err
 		}
@@ -129,7 +131,6 @@ func parseV3(files []string) (kobject.KomposeObject, error) {
 	if err != nil {
 		return kobject.KomposeObject{}, err
 	}
-
 	return komposeObject, nil
 }
 
diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go
index 534584e1..a1b7823e 100644
--- a/pkg/transformer/kubernetes/k8sutils.go
+++ b/pkg/transformer/kubernetes/k8sutils.go
@@ -25,6 +25,7 @@ import (
 	"path"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"sort"
 	"strconv"
 	"strings"
@@ -382,6 +383,7 @@ func (k *Kubernetes) initSvcObject(name string, service kobject.ServiceConfig, p
 	return svc
 }
 
+// CreateLBService creates a k8s Load Balancer Service
 func (k *Kubernetes) CreateLBService(name string, service kobject.ServiceConfig) []*api.Service {
 	var svcs []*api.Service
 	tcpPorts, udpPorts := k.ConfigLBServicePorts(service)
@@ -442,6 +444,8 @@ func (k *Kubernetes) CreateHeadlessService(name string, service kobject.ServiceC
 
 	return svc
 }
+
+// UpdateKubernetesObjectsMultipleContainers method updates the kubernetes objects with the necessary data
 func (k *Kubernetes) UpdateKubernetesObjectsMultipleContainers(name string, service kobject.ServiceConfig, objects *[]runtime.Object, podSpec PodSpec) error {
 	// Configure annotations
 	annotations := transformer.ConfigAnnotations(service)
@@ -524,7 +528,7 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
 		template.Spec.Containers[0].Name = GetContainerName(service)
 		template.Spec.Containers[0].Env = envs
 		template.Spec.Containers[0].Command = service.Command
-		template.Spec.Containers[0].Args = service.Args
+		template.Spec.Containers[0].Args = GetContainerArgs(service)
 		template.Spec.Containers[0].WorkingDir = service.WorkingDir
 		template.Spec.Containers[0].VolumeMounts = append(template.Spec.Containers[0].VolumeMounts, volumesMount...)
 		template.Spec.Containers[0].Stdin = service.Stdin
@@ -594,18 +598,18 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
 		template.ObjectMeta.Labels = transformer.ConfigLabelsWithNetwork(name, service.Network)
 
 		// Configure the image pull policy
-		if policy, err := GetImagePullPolicy(name, service.ImagePullPolicy); err != nil {
+		policy, err := GetImagePullPolicy(name, service.ImagePullPolicy)
+		if err != nil {
 			return err
-		} else {
-			template.Spec.Containers[0].ImagePullPolicy = policy
 		}
+		template.Spec.Containers[0].ImagePullPolicy = policy
 
 		// Configure the container restart policy.
-		if restart, err := GetRestartPolicy(name, service.Restart); err != nil {
+		restart, err := GetRestartPolicy(name, service.Restart)
+		if err != nil {
 			return err
-		} else {
-			template.Spec.RestartPolicy = restart
 		}
+		template.Spec.RestartPolicy = restart
 
 		// Configure hostname/domain_name settings
 		if service.HostName != "" {
@@ -887,6 +891,7 @@ func FormatContainerName(name string) string {
 	return name
 }
 
+// GetContainerName returns the name of the container, from the service config object
 func GetContainerName(service kobject.ServiceConfig) string {
 	name := service.Name
 	if len(service.ContainerName) > 0 {
@@ -899,3 +904,15 @@ func GetContainerName(service kobject.ServiceConfig) string {
 func FormatResourceName(name string) string {
 	return strings.ToLower(strings.Replace(name, "_", "-", -1))
 }
+
+// GetContainerArgs update the interpolation of env variables if exists.
+// example: [curl, $PROTOCOL://$DOMAIN] => [curl, $(PROTOCOL)://$(DOMAIN)]
+func GetContainerArgs(service kobject.ServiceConfig) []string {
+	var args []string
+	re := regexp.MustCompile(`\$([a-zA-Z0-9]*)`)
+	for _, arg := range service.Args {
+		arg = re.ReplaceAllString(arg, `$($1)`)
+		args = append(args, arg)
+	}
+	return args
+}
diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go
index 5de64d45..29635672 100644
--- a/pkg/transformer/kubernetes/kubernetes.go
+++ b/pkg/transformer/kubernetes/kubernetes.go
@@ -58,6 +58,7 @@ type Kubernetes struct {
 // PVCRequestSize (Persistent Volume Claim) has default size
 const PVCRequestSize = "100Mi"
 
+// ValidVolumeSet has the different types of valid volumes
 var ValidVolumeSet = map[string]struct{}{"emptyDir": {}, "hostPath": {}, "configMap": {}, "persistentVolumeClaim": {}}
 
 const (
@@ -432,6 +433,7 @@ func (k *Kubernetes) InitDS(name string, service kobject.ServiceConfig) *appsv1.
 	return ds
 }
 
+// InitSS method initialize a stateful set
 func (k *Kubernetes) InitSS(name string, service kobject.ServiceConfig, replicas int) *appsv1.StatefulSet {
 	var podSpec api.PodSpec
 	if len(service.Configs) > 0 {
@@ -634,6 +636,7 @@ func ConfigPorts(service kobject.ServiceConfig) []api.ContainerPort {
 	return ports
 }
 
+// ConfigLBServicePorts method configure the ports of the k8s Load Balancer Service
 func (k *Kubernetes) ConfigLBServicePorts(service kobject.ServiceConfig) ([]api.ServicePort, []api.ServicePort) {
 	var tcpPorts []api.ServicePort
 	var udpPorts []api.ServicePort
@@ -954,16 +957,15 @@ func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) (
 			volsource = source
 		} else if useConfigMap {
 			log.Debugf("Use configmap volume")
-
-			if cm, err := k.IntiConfigMapFromFileOrDir(name, volumeName, volume.Host, service); err != nil {
+			cm, err := k.IntiConfigMapFromFileOrDir(name, volumeName, volume.Host, service)
+			if err != nil {
 				return nil, nil, nil, nil, err
-			} else {
-				cms = append(cms, cm)
-				volsource = k.ConfigConfigMapVolumeSource(volumeName, volume.Container, cm)
+			}
+			cms = append(cms, cm)
+			volsource = k.ConfigConfigMapVolumeSource(volumeName, volume.Container, cm)
 
-				if useSubPathMount(cm) {
-					volMount.SubPath = volsource.ConfigMap.Items[0].Path
-				}
+			if useSubPathMount(cm) {
+				volMount.SubPath = volsource.ConfigMap.Items[0].Path
 			}
 		} else {
 			volsource = k.ConfigPVCVolumeSource(volumeName, readonly)
diff --git a/pkg/transformer/kubernetes/podspec.go b/pkg/transformer/kubernetes/podspec.go
index 7531b3eb..d5237841 100644
--- a/pkg/transformer/kubernetes/podspec.go
+++ b/pkg/transformer/kubernetes/podspec.go
@@ -13,12 +13,15 @@ import (
 	"k8s.io/apimachinery/pkg/util/intstr"
 )
 
+// PodSpec holds the spec of k8s pod.
 type PodSpec struct {
 	api.PodSpec
 }
 
+// PodSpecOption holds the function to apply on a PodSpec
 type PodSpecOption func(*PodSpec)
 
+// AddContainer method is responsible for adding a new container to a k8s Pod.
 func AddContainer(service kobject.ServiceConfig, opt kobject.ConvertOptions) PodSpecOption {
 	return func(podSpec *PodSpec) {
 		name := GetContainerName(service)
@@ -50,6 +53,7 @@ func AddContainer(service kobject.ServiceConfig, opt kobject.ConvertOptions) Pod
 	}
 }
 
+// TerminationGracePeriodSeconds method is responsible for attributing the grace period seconds option to a pod
 func TerminationGracePeriodSeconds(name string, service kobject.ServiceConfig) PodSpecOption {
 	return func(podSpec *PodSpec) {
 		var err error
@@ -156,6 +160,7 @@ func SecurityContext(name string, service kobject.ServiceConfig) PodSpecOption {
 	}
 }
 
+// SetVolumeNames method return a set of volume names
 func SetVolumeNames(volumes []api.Volume) mapset.Set {
 	set := mapset.NewSet()
 	for _, volume := range volumes {
@@ -164,6 +169,7 @@ func SetVolumeNames(volumes []api.Volume) mapset.Set {
 	return set
 }
 
+// SetVolumes method returns a method that adds the volumes to the pod spec
 func SetVolumes(volumes []api.Volume) PodSpecOption {
 	return func(podSpec *PodSpec) {
 		volumesSet := SetVolumeNames(volumes)
@@ -179,6 +185,7 @@ func SetVolumes(volumes []api.Volume) PodSpecOption {
 	}
 }
 
+// SetVolumeMountPaths method returns a set of volumes mount path
 func SetVolumeMountPaths(volumesMount []api.VolumeMount) mapset.Set {
 	set := mapset.NewSet()
 	for _, volumeMount := range volumesMount {
@@ -188,6 +195,7 @@ func SetVolumeMountPaths(volumesMount []api.VolumeMount) mapset.Set {
 	return set
 }
 
+// SetVolumeMounts returns a function which adds the volume mounts option to the pod spec
 func SetVolumeMounts(volumesMount []api.VolumeMount) PodSpecOption {
 	return func(podSpec *PodSpec) {
 		volumesMountSet := SetVolumeMountPaths(volumesMount)
@@ -242,6 +250,7 @@ func RestartPolicy(name string, service kobject.ServiceConfig) PodSpecOption {
 	}
 }
 
+// HostName configure the host name of a pod
 func HostName(service kobject.ServiceConfig) PodSpecOption {
 	return func(podSpec *PodSpec) {
 		// Configure hostname/domain_name settings
@@ -251,6 +260,7 @@ func HostName(service kobject.ServiceConfig) PodSpecOption {
 	}
 }
 
+// DomainName configure the domain name of a pod
 func DomainName(service kobject.ServiceConfig) PodSpecOption {
 	return func(podSpec *PodSpec) {
 		if service.DomainName != "" {
@@ -299,18 +309,21 @@ func configProbe(healthCheck kobject.HealthCheck) *api.Probe {
 	return &probe
 }
 
+// ServiceAccountName is responsible for setting the service account name to the pod spec
 func ServiceAccountName(serviceAccountName string) PodSpecOption {
 	return func(podSpec *PodSpec) {
 		podSpec.ServiceAccountName = serviceAccountName
 	}
 }
 
+// TopologySpreadConstraints is responsible for setting the topology spread constraints to the pod spec
 func TopologySpreadConstraints(service kobject.ServiceConfig) PodSpecOption {
 	return func(podSpec *PodSpec) {
 		podSpec.TopologySpreadConstraints = ConfigTopologySpreadConstraints(service)
 	}
 }
 
+// Append is responsible for adding the pod spec options to the particular pod
 func (podSpec *PodSpec) Append(ops ...PodSpecOption) *PodSpec {
 	for _, option := range ops {
 		option(podSpec)
@@ -318,6 +331,7 @@ func (podSpec *PodSpec) Append(ops ...PodSpecOption) *PodSpec {
 	return podSpec
 }
 
+// Get is responsible for returning the pod spec of a particular pod
 func (podSpec *PodSpec) Get() api.PodSpec {
 	return podSpec.PodSpec
 }
diff --git a/pkg/utils/docker/image.go b/pkg/utils/docker/image.go
index edd1ec48..f9cf4c68 100644
--- a/pkg/utils/docker/image.go
+++ b/pkg/utils/docker/image.go
@@ -33,6 +33,7 @@ type Image struct {
 	Remote     string // the image's remote identifier. (ie: registry/name[:tag])
 }
 
+// NewImageFromParsed method returns the docker image from the docker parser reference
 func NewImageFromParsed(parsed *dockerparser.Reference) Image {
 	return Image{
 		Name:       parsed.Name(),
diff --git a/pkg/utils/docker/tag.go b/pkg/utils/docker/tag.go
index 6c0e6f07..482617b7 100644
--- a/pkg/utils/docker/tag.go
+++ b/pkg/utils/docker/tag.go
@@ -27,6 +27,7 @@ type Tag struct {
 	Client dockerlib.Client
 }
 
+// TagImage function is responsible for tagging the docker image
 func (c *Tag) TagImage(image Image) error {
 	options := dockerlib.TagImageOptions{
 		Tag:  image.Tag,
diff --git a/pkg/version/version.go b/pkg/version/version.go
index de69ff0e..975de509 100644
--- a/pkg/version/version.go
+++ b/pkg/version/version.go
@@ -2,7 +2,7 @@ package version
 
 var (
 	// VERSION  is version number that will be displayed when running ./kompose version
-	VERSION = "1.26.1"
+	VERSION = "1.27.0"
 	// GITCOMMIT is hash of the commit that will be displayed when running ./kompose version
 	// this will be overwritten when running  build like this: go build -ldflags="-X github.com/kubernetes/kompose/pkg/version.GITCOMMIT=$(GITCOMMIT)"
 	// HEAD is default indicating that this was not set during build
diff --git a/script/release.sh b/script/release.sh
index dd1b0a16..a498f080 100755
--- a/script/release.sh
+++ b/script/release.sh
@@ -23,11 +23,7 @@ usage() {
   echo ""
   echo "Requirements:"
   echo " git"
-  echo " hub"
-  echo " github-release"
-  echo " github_changelog_generator"
-  echo " fpm"
-  echo " GITHUB_TOKEN in your env variable"
+  echo " gh"
   echo " "
   echo "Not only that, but you must have permission for:"
   echo " Tagging releases within Github"
@@ -41,30 +37,6 @@ requirements() {
     exit 0
   fi
 
-  if ! hash github-release 2>/dev/null; then
-    echo "ERROR: No $GOPATH/bin/github-release. Please run 'go get -v github.com/aktau/github-release'"
-    exit 0
-  fi
-
-  if ! hash github_changelog_generator 2>/dev/null; then
-    echo "ERROR: github_changelog_generator required to generate the change log. Please run 'gem install github_changelog_generator"
-    exit 0
-  fi
-
-  if ! hash fpm 2>/dev/null; then
-    echo "ERROR: fpm required to generate deb/rpm packages. Please run 'gem install fpm"
-    exit 0
-  fi
-
-  if ! hash hub 2>/dev/null; then
-    echo "ERROR: Hub needed in order to create the relevant PR's. Please install hub @ https://github.com/github/hub"
-    exit 0
-  fi
-
-  if [[ -z "$GITHUB_TOKEN" ]]; then
-    echo "ERROR: export GITHUB_TOKEN=yourtoken needed for using github-release"
-    exit 0
-  fi
 }
 
 # Make sure that upstream had been added to the repo 
@@ -103,62 +75,15 @@ replaceversion() {
 
   echo "Replaced version in build/VERSION"
   sed -i "s/$1/$2/g" build/VERSION || gsed -i "s/$1/$2/g" build/VERSION
-}
 
-changelog() {
-  echo "Generating changelog using github-changelog-generator"
-  github_changelog_generator --user $UPSTREAM_REPO --project $CLI -t $GITHUB_TOKEN --future-release v$1
-}
 
-changelog_github() {
-  touch changes.txt
-  echo "Write your GitHub changelog here" >> changes.txt
-  $EDITOR changes.txt
+  echo "Ignore above errors if you're using macOS"
 }
 
 build_binaries() {
   make cross
 }
 
-build_packages() {
-  # fpm is required installed (https://github.com/jordansissel/fpm)
-  BIN_DIR="./bin/"
-  PKG_DIR="./bin/"
-
-  mkdir -p $PKG_DIR
-
-  # package version, use current date by default (if build from master)
-  PKG_VERSION=$1
-
-  # create packages using fpm
-  fpm -h  >/dev/null 2>&1 || { 
-      echo "ERROR: fpm (https://github.com/jordansissel/fpm) is not installed. Can't create linux packages"
-      exit 1
-  }
-
-  TMP_DIR=$(mktemp -d)
-  mkdir -p $TMP_DIR/usr/local/bin/
-  cp $BIN_DIR/kompose-linux-amd64 $TMP_DIR/usr/local/bin/kompose
-
-  echo "creating DEB package"
-  fpm \
-    --input-type dir --output-type deb \
-    --chdir $TMP_DIR \
-    --name kompose --version $PKG_VERSION \
-    --architecture amd64 \
-    --maintainer "Charlie Drage <cdrage@redhat.com>" \
-    --package $PKG_DIR
-
-  echo "creating RPM package"
-  fpm \
-    --input-type dir --output-type rpm \
-    --chdir $TMP_DIR \
-    --name kompose --version $PKG_VERSION \
-    --architecture x86_64 --rpm-os linux \
-    --maintainer "Charlie Drage <cdrage@redhat.com>" \
-    --package $PKG_DIR
-}
-
 create_tarballs() {
   # cd into the bin directory so we don't have '/bin' inside the tarball
   cd bin
@@ -180,7 +105,7 @@ git_commit() {
   git add .
   git commit -m "$1 Release"
   git push origin $BRANCH
-  hub pull-request -b $UPSTREAM_REPO/$CLI:master -h $ORIGIN_REPO/$CLI:$BRANCH
+  gh pr
 
   echo ""
   echo "PR opened against master to update version"
@@ -198,10 +123,6 @@ git_sync() {
   git rebase upstream/master
 }
 
-git_tag() {
-  git tag v$1
-}
-
 generate_install_guide() {
   echo "
 # Installation
@@ -238,46 +159,17 @@ __Checksums:__
     echo "$HASH $NAME" >> bin/SHA256_SUM
   done
 
- # Append the file to the file
- cat install_guide.txt >> changes.txt
 }
 
 push() {
-  CHANGES=$(cat changes.txt)
-  # Release it!
-
-  echo "Creating GitHub tag"
-  github-release release \
-      --user $UPSTREAM_REPO \
-      --repo $CLI \
-      --tag v$1 \
-      --name "v$1" \
-      --description "$CHANGES"
-  if [ $? -eq 0 ]; then
-        echo UPLOAD OK 
-  else 
-        echo UPLOAD FAIL
-        exit
-  fi
 
-  # Upload all the binaries and tarballs generated in bin/
-  for f in bin/*
-  do
-    echo "Uploading file $f"
-    NAME=`echo $f | sed "s,bin/,,g"`
-    github-release upload \
-        --user $UPSTREAM_REPO \
-        --repo $CLI \
-        --tag v$1 \
-        --file $f \
-        --name $NAME
-    if [ $? -eq 0 ]; then
-          echo UPLOAD OK 
-    else 
-          echo UPLOAD FAIL
-          exit
-    fi
-  done
+  echo "!!PLEASE READ!!
+  1. Say YES to GitHub generating the release notes
+  2. Append install_guide.txt to the TOP of the release notes when prompted
+  3. Double check that the binaries have been UPLOADED!
+  "
+
+  gh release create v$1 bin/*
 
   echo "DONE"
   echo "DOUBLE CHECK IT:"
@@ -287,7 +179,7 @@ push() {
 }
 
 clean() {
-  rm changes.txt install_guide.txt
+  rm install_guide.txt
   rm -r bin/*
 }
 
@@ -325,11 +217,8 @@ main() {
   options=(
   "Initial sync with upstream"
   "Replace version number"
-  "Generate changelog"
-  "Generate GitHub changelog"
   "Create PR"
   "Sync with upstream"
-  "Create tag"
   "Build binaries"
   "Build packages"
   "Create tarballs"
@@ -347,27 +236,15 @@ main() {
           "Replace version number")
               replaceversion $PREV_VERSION $VERSION
               ;;
-          "Generate changelog")
-              changelog $VERSION
-              ;;
-          "Generate GitHub changelog")
-              changelog_github $VERSION
-              ;;
           "Create PR")
               git_commit $VERSION
               ;;
           "Sync with upstream")
               git_sync
               ;;
-          "Create tag")
-              git_tag $VERSION
-              ;;
           "Build binaries")
               build_binaries
               ;;
-          "Build packages")
-              build_packages $VERSION
-              ;;
           "Create tarballs")
               create_tarballs
               ;;


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 8eec4a0a..f26a17f7 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -43,5 +43,3 @@ jobs:
         env:
           # As per https://github.com/mattn/goveralls#github-actions
           COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      - run: ./script/sync-docs.sh
-        if: ${{ github.ref == 'refs/heads/master' }}
diff --git a/pkg/transformer/kubernetes/k8sutils_test.go b/pkg/transformer/kubernetes/k8sutils_test.go
index 22aae955..e9358905 100644
--- a/pkg/transformer/kubernetes/k8sutils_test.go
+++ b/pkg/transformer/kubernetes/k8sutils_test.go
@@ -597,3 +597,35 @@ func TestCreateServiceWithSpecialName(t *testing.T) {
 		}
 	}
 }
+
+func TestArgsInterpolation(t *testing.T) {
+	// An example service
+	service := kobject.ServiceConfig{
+		ContainerName: "name",
+		Image:         "image",
+		Environment:   []kobject.EnvVar{{Name: "PROTOCOL", Value: "https"}, {Name: "DOMAIN", Value: "google.com"}},
+		Port:          []kobject.Ports{{HostPort: 123, ContainerPort: 456, Protocol: string(corev1.ProtocolTCP)}},
+		Command:       []string{"curl"},
+		Args:          []string{"$PROTOCOL://$DOMAIN/"},
+	}
+
+	// An example object generated via k8s runtime.Objects()
+	komposeObject := kobject.KomposeObject{
+		ServiceConfigs: map[string]kobject.ServiceConfig{"app": service},
+	}
+	k := Kubernetes{}
+	objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 3})
+	if err != nil {
+		t.Error(errors.Wrap(err, "k.Transform failed"))
+	}
+
+	expectedArgs := []string{"$(PROTOCOL)://$(DOMAIN)/"}
+	for _, obj := range objects {
+		if deployment, ok := obj.(*appsv1.Deployment); ok {
+			args := deployment.Spec.Template.Spec.Containers[0].Args[0]
+			if args != expectedArgs[0] {
+				t.Errorf("Expected args %v upon conversion, actual %v", expectedArgs, args)
+			}
+		}
+	}
+}
diff --git a/script/test/cmd/tests_new.sh b/script/test/cmd/tests_new.sh
index f63e5ad7..34a10185 100755
--- a/script/test/cmd/tests_new.sh
+++ b/script/test/cmd/tests_new.sh
@@ -202,4 +202,12 @@ os_cmd="kompose  --provider=openshift -f  $KOMPOSE_ROOT/script/test/fixtures/mul
 k8s_output="$KOMPOSE_ROOT/script/test/fixtures/multiple-type-volumes/output-k8s.json"
 os_output="$KOMPOSE_ROOT/script/test/fixtures/multiple-type-volumes/output-os.json"
 convert::expect_success_and_warning "$k8s_cmd" "$k8s_output"
-convert::expect_success "$os_cmd" "$os_output"
\ No newline at end of file
+convert::expect_success "$os_cmd" "$os_output"
+
+# Test environment variables interpolation
+k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/envvars-interpolation/docker-compose.yaml convert --stdout -j --with-kompose-annotation=false"
+os_cmd="kompose  --provider=openshift -f  $KOMPOSE_ROOT/script/test/fixtures/envvars-interpolation/docker-compose.yaml  convert --stdout -j --with-kompose-annotation=false"
+k8s_output="$KOMPOSE_ROOT/script/test/fixtures/envvars-interpolation/output-k8s.json"
+os_output="$KOMPOSE_ROOT/script/test/fixtures/envvars-interpolation/output-os.json"
+convert::expect_success_and_warning "$k8s_cmd" "$k8s_output"
+convert::expect_success "$os_cmd" "$os_output"
diff --git a/script/test/fixtures/envvars-interpolation/docker-compose.yaml b/script/test/fixtures/envvars-interpolation/docker-compose.yaml
new file mode 100644
index 00000000..6623f589
--- /dev/null
+++ b/script/test/fixtures/envvars-interpolation/docker-compose.yaml
@@ -0,0 +1,13 @@
+version: '3.5'
+
+services:
+    myservice:
+        image: alpine
+        environment:
+            PROTOCOL: 'https'
+            DOMAIN: 'google.com'
+        command:
+            [
+                'curl',
+                '$PROTOCOL://$DOMAIN/',
+            ]
diff --git a/script/test/fixtures/envvars-interpolation/output-k8s.json b/script/test/fixtures/envvars-interpolation/output-k8s.json
new file mode 100644
index 00000000..6ee8b10b
--- /dev/null
+++ b/script/test/fixtures/envvars-interpolation/output-k8s.json
@@ -0,0 +1,60 @@
+{
+    "kind": "List",
+    "apiVersion": "v1",
+    "metadata": {},
+    "items": [
+        {
+            "kind": "Deployment",
+            "apiVersion": "apps/v1",
+            "metadata": {
+                "name": "myservice",
+                "creationTimestamp": null,
+                "labels": {
+                    "io.kompose.service": "myservice"
+                }
+            },
+            "spec": {
+                "replicas": 1,
+                "selector": {
+                    "matchLabels": {
+                        "io.kompose.service": "myservice"
+                    }
+                },
+                "template": {
+                    "metadata": {
+                        "creationTimestamp": null,
+                        "labels": {
+                            "io.kompose.service": "myservice"
+                        }
+                    },
+                    "spec": {
+                        "containers": [
+                            {
+                                "name": "myservice",
+                                "image": "alpine",
+                                "args": [
+                                    "curl",
+                                    "$(PROTOCOL)://$(DOMAIN)/"
+                                ],
+                                "env": [
+                                    {
+                                        "name": "DOMAIN",
+                                        "value": "google.com"
+                                    },
+                                    {
+                                        "name": "PROTOCOL",
+                                        "value": "https"
+                                    }
+                                ],
+                                "resources": {}
+                            }
+                        ],
+                        "restartPolicy": "Always"
+                    }
+                },
+                "strategy": {}
+            },
+            "status": {}
+        }
+    ]
+}
diff --git a/script/test/fixtures/envvars-interpolation/output-os.json b/script/test/fixtures/envvars-interpolation/output-os.json
new file mode 100644
index 00000000..8f7796f8
--- /dev/null
+++ b/script/test/fixtures/envvars-interpolation/output-os.json
@@ -0,0 +1,120 @@
+{
+    "kind": "List",
+    "apiVersion": "v1",
+    "metadata": {},
+    "items": [
+        {
+            "kind": "DeploymentConfig",
+            "apiVersion": "v1",
+            "metadata": {
+                "name": "myservice",
+                "creationTimestamp": null,
+                "labels": {
+                    "io.kompose.service": "myservice"
+                }
+            },
+            "spec": {
+                "strategy": {
+                    "resources": {}
+                },
+                "triggers": [
+                    {
+                        "type": "ConfigChange"
+                    },
+                    {
+                        "type": "ImageChange",
+                        "imageChangeParams": {
+                            "automatic": true,
+                            "containerNames": [
+                                "myservice"
+                            ],
+                            "from": {
+                                "kind": "ImageStreamTag",
+                                "name": "myservice:latest"
+                            }
+                        }
+                    }
+                ],
+                "replicas": 1,
+                "test": false,
+                "selector": {
+                    "io.kompose.service": "myservice"
+                },
+                "template": {
+                    "metadata": {
+                        "creationTimestamp": null,
+                        "labels": {
+                            "io.kompose.service": "myservice"
+                        }
+                    },
+                    "spec": {
+                        "containers": [
+                            {
+                                "name": "myservice",
+                                "image": " ",
+                                "args": [
+                                    "curl",
+                                    "$(PROTOCOL)://$(DOMAIN)/"
+                                ],
+                                "env": [
+                                    {
+                                        "name": "DOMAIN",
+                                        "value": "google.com"
+                                    },
+                                    {
+                                        "name": "PROTOCOL",
+                                        "value": "https"
+                                    }
+                                ],
+                                "resources": {}
+                            }
+                        ],
+                        "restartPolicy": "Always"
+                    }
+                }
+            },
+            "status": {
+                "latestVersion": 0,
+                "observedGeneration": 0,
+                "replicas": 0,
+                "updatedReplicas": 0,
+                "availableReplicas": 0,
+                "unavailableReplicas": 0
+            }
+        },
+        {
+            "kind": "ImageStream",
+            "apiVersion": "v1",
+            "metadata": {
+                "name": "myservice",
+                "creationTimestamp": null,
+                "labels": {
+                    "io.kompose.service": "myservice"
+                }
+            },
+            "spec": {
+                "lookupPolicy": {
+                    "local": false
+                },
+                "tags": [
+                    {
+                        "name": "latest",
+                        "annotations": null,
+                        "from": {
+                            "kind": "DockerImage",
+                            "name": "alpine"
+                        },
+                        "generation": null,
+                        "importPolicy": {},
+                        "referencePolicy": {
+                            "type": ""
+                        }
+                    }
+                ]
+            },
+            "status": {
+                "dockerImageRepository": ""
+            }
+        }
+    ]
+}