func setupCronFile(workspace string, vargs GAE) error {
	return setupFile(workspace, vargs, "cron.yaml", vargs.CronFile)
}
Useful for differentiating between prd and dev cron versions for GCP appengine
package main
import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"
	"github.com/drone/drone-plugin-go/plugin"
)
type GAE struct {
	Action string `json:"action"`
	AddlArgs map[string]string `json:"addl_args"`
	AddlFlags []string `json:"addl_flags"`
	Version string `json:"version"`
	Service string `json:"service"`
	AEEnv map[string]string `json:"ae_environment"`
	SubCommands []string `json:"sub_commands"`
	FlexImage string `json:"flex_image"`
	TemplateVars map[string]interface{} `json:"vars"`
	AppFile string `json:"app_file"`
	MaxVersions int `json:"max_versions"`
	CronFile string `json:"cron_file"`
	DispatchFile string `json:"dispatch_file"`
	QueueFile string `json:"queue_file"`
	Dir string `json:"dir"`
	Project string `json:"project"`
	Token string `json:"token"`
	GCloudCmd string `json:"gcloud_cmd"`
	AppCfgCmd string `json:"appcfg_cmd"`
	Beta bool `json:"beta"`
}
var (
	rev string
)
func main() {
	err := wrapMain()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
func wrapMain() error {
	if rev == "" {
		rev = "[unknown]"
	}
	fmt.Printf("Drone GAE Plugin built from %s\n", rev)
	vargs := GAE{}
	workspace := ""
	if os.Getenv("DRONE_WORKSPACE") == "" {  
		err := configFromStdin(&vargs, &workspace)
		if err != nil {
			return err
		}
	} else {  
		err := configFromEnv(&vargs, &workspace)
		if err != nil {
			return err
		}
	}
	err := validateVargs(&vargs)
	if err != nil {
		return err
	}
	keyPath := "/tmp/gcloud.json"
	vargs.Token = strings.TrimSpace(vargs.Token)
	err = ioutil.WriteFile(keyPath, []byte(vargs.Token), 0600)
	if err != nil {
		return fmt.Errorf("error writing token file: %s\n", err)
	}
	defer func() {
		err := os.Remove(keyPath)
		if err != nil {
			fmt.Printf("warning: error removing token file: %s\n", err)
		}
	}()
	runner := NewEnviron(filepath.Join(workspace, vargs.Dir), os.Environ(),
		os.Stdout, os.Stderr)
	err = runner.Run(vargs.GCloudCmd, "auth", "activate-service-account", "--key-file", keyPath)
	if err != nil {
		return fmt.Errorf("error: %s\n", err)
	}
	if gcloudCmds[vargs.Action] || gcloudGroups[vargs.Action] {
		err = runGcloud(runner, workspace, vargs)
	} else {
		err = runAppCfg(runner, workspace, vargs)
	}
	if err != nil {
		return err
	}
	if vargs.MaxVersions > 0 && (vargs.Action == "deploy" || vargs.Action == "update") {
		return removeOldVersions(runner, workspace, vargs)
	}
	return nil
}
func configFromStdin(vargs *GAE, workspace *string) error {
	workspaceInfo := plugin.Workspace{}
	plugin.Param("workspace", &workspaceInfo)
	plugin.Param("vargs", vargs)
	plugin.MustParse()
	*workspace = workspaceInfo.Path
	return nil
}
type dummyGAE struct {
	AddlArgs     map[string]string      `json:"-"`
	AEEnv        map[string]string      `json:"-"`
	TemplateVars map[string]interface{} `json:"-"`
}
func configFromEnv(vargs *GAE, workspace *string) error {
	*workspace = os.Getenv("DRONE_WORKSPACE")
	vargs.Action = os.Getenv("PLUGIN_ACTION")
	vargs.Version = os.Getenv("PLUGIN_VERSION")
	vargs.Service = os.Getenv("PLUGIN_SERVICE")
	vargs.FlexImage = os.Getenv("PLUGIN_FLEX_IMAGE")
	vargs.AppFile = os.Getenv("PLUGIN_APP_FILE")
	vargs.MaxVersions, _ = strconv.Atoi(os.Getenv("PLUGIN_MAX_VERSIONS"))
	vargs.CronFile = os.Getenv("PLUGIN_CRON_FILE")
	vargs.DispatchFile = os.Getenv("PLUGIN_DISPATCH_FILE")
	vargs.QueueFile = os.Getenv("PLUGIN_QUEUE_FILE")
	vargs.Dir = os.Getenv("PLUGIN_DIR")
	vargs.Project = os.Getenv("PLUGIN_PROJECT")
	vargs.GCloudCmd = os.Getenv("PLUGIN_GCLOUD_CMD")
	vargs.AppCfgCmd = os.Getenv("PLUGIN_APPCFG_CMD")
	vargs.Beta = os.Getenv("PLUGIN_BETA") == "true"
	vargs.Token = os.Getenv("PLUGIN_GAE_CREDENTIALS")
	if vargs.Token == "" {
		vargs.Token = os.Getenv("GAE_CREDENTIALS")
	}
	if decodedToken, err := base64.StdEncoding.DecodeString(vargs.Token); err == nil {
		vargs.Token = string(decodedToken)
	} else {
		fmt.Println("gae_credentials is not base64 encoded: skipping decode")
	}
	dummyVargs := dummyGAE{}
	addlArgs := os.Getenv("PLUGIN_ADDL_ARGS")
	if addlArgs != "" {
		if err := json.Unmarshal([]byte(addlArgs), &dummyVargs.AddlArgs); err != nil {
			return fmt.Errorf("could not parse param addl_args into a map[string]string")
		}
		vargs.AddlArgs = dummyVargs.AddlArgs
	}
	AEEnv := os.Getenv("PLUGIN_AE_ENVIRONMENT")
	if AEEnv != "" {
		if err := json.Unmarshal([]byte(AEEnv), &dummyVargs.AEEnv); err != nil {
			return fmt.Errorf("could not parse param ae_environment into a map[string]string")
		}
		for k, v := range dummyVargs.AEEnv {
			if s := os.ExpandEnv(v); s != "" {
				dummyVargs.AEEnv[k] = os.ExpandEnv(v)
			}
		}
		vargs.AEEnv = dummyVargs.AEEnv
	}
	templateVars := os.Getenv("PLUGIN_VARS")
	if templateVars != "" {
		if err := json.Unmarshal([]byte(templateVars), &dummyVargs.TemplateVars); err != nil {
			return fmt.Errorf("could not parse param vars into a map[string]interface{}")
		}
		for k, v := range dummyVargs.TemplateVars {
			if v, ok := v.(string); ok {
				if s := os.ExpandEnv(v); s != "" {
					dummyVargs.TemplateVars[k] = os.ExpandEnv(v)
				}
			}
		}
		vargs.TemplateVars = dummyVargs.TemplateVars
	}
	vargs.AddlFlags = strings.Split(os.Getenv("PLUGIN_ADDL_FLAGS"), ",")
	vargs.SubCommands = strings.Split(os.Getenv("PLUGIN_SUB_COMMANDS"), ",")
	return nil
}
func validateVargs(vargs *GAE) error {
	if vargs.Token == "" {
		return fmt.Errorf("missing required credentials: GAE_CREDENTIALS or param token")
	}
	if vargs.Project == "" {
		vargs.Project = getProjectFromToken(vargs.Token)
		if vargs.Project == "" {
			return fmt.Errorf("missing required project id: not found in credentials or param project")
		}
	}
	if vargs.Action == "" {
		return fmt.Errorf("missing required param: action")
	}
	if vargs.AppCfgCmd == "" {
		vargs.AppCfgCmd = "/go_appengine/appcfg.py"
	}
	if vargs.GCloudCmd == "" {
		vargs.GCloudCmd = "gcloud"
	}
	if vargs.Version != "" {
		v := strings.ToLower(vargs.Version)
		if len(v) > 63 {
			v = v[:63]
		}
		re := regexp.MustCompile(`[^a-z\d-]`)
		vargs.Version = re.ReplaceAllString(v, "-")
	}
	return nil
}
var gcloudGroups = map[string]bool{
	"services":  true,
	"versions":  true,
	"instances": true,
}
var gcloudCmds = map[string]bool{
	"deploy": true,
}
func runGcloud(runner *Environ, workspace string, vargs GAE) error {
	var args []string
	if vargs.Beta {
		args = append(args, "beta")
	}
	args = append(args, []string{
		"app",
		vargs.Action,
	}...)
	for _, cmd := range vargs.SubCommands {
		if len(cmd) > 0 {
			args = append(args, cmd)
		}
	}
	if !gcloudGroups[vargs.Action] {
		switch {
		case vargs.DispatchFile != "":
			args = append(args, "./dispatch.yaml")
		case vargs.CronFile != "":
			args = append(args, "./cron.yaml")
		default:
			args = append(args, "./app.yaml")
		}
	}
	if vargs.Version != "" {
		if gcloudCmds[vargs.Action] {
			args = append(args, "--version", vargs.Version)
		} else {
			args = append(args, vargs.Version)
		}
	}
	if vargs.Service != "" {
		if gcloudCmds[vargs.Action] {
			args = append(args, "--service", vargs.Service)
		} else {
			args = append(args, vargs.Service)
		}
	}
	if vargs.FlexImage != "" {
		args = append(args, "--image-url", vargs.FlexImage)
	}
	if len(vargs.Project) > 0 {
		args = append(args, "--project", vargs.Project)
	}
	args = append(args, "--quiet")
	if len(vargs.AddlArgs) > 0 {
		for k, v := range vargs.AddlArgs {
			args = append(args, k, v)
		}
	}
	if len(vargs.AddlFlags) > 0 {
		for _, v := range vargs.AddlFlags {
			if len(v) > 0 {
				args = append(args, v)
			}
		}
	}
	if err := setupAppFile(workspace, vargs); err != nil {
		return err
	}
	if err := setupCronFile(workspace, vargs); err != nil {
		return err
	}
	if err := setupDispatchFile(workspace, vargs); err != nil {
		return err
	}
	if err := setupQueueFile(workspace, vargs); err != nil {
		return err
	}
	err := runner.Run(vargs.GCloudCmd, args...)
	if err != nil {
		return fmt.Errorf("error: %s\n", err)
	}
	return nil
}
func runAppCfg(runner *Environ, workspace string, vargs GAE) error {
	tokenCmd := exec.Command(vargs.GCloudCmd, "auth", "print-access-token")
	var accessToken bytes.Buffer
	tokenCmd.Stdout = &accessToken
	err := tokenCmd.Run()
	if err != nil {
		return fmt.Errorf("error creating access token: %s\n", err)
	}
	args := []string{
		"--oauth2_access_token", accessToken.String(),
		"-A", vargs.Project,
	}
	if vargs.Version != "" {
		args = append(args, "-V", vargs.Version)
	}
	if len(vargs.AEEnv) > 0 {
		for k, v := range vargs.AEEnv {
			args = append(args, "-E", k+":"+v)
		}
	}
	if len(vargs.AddlArgs) > 0 {
		for k, v := range vargs.AddlArgs {
			args = append(args, k, v)
		}
	}
	args = append(args, vargs.Action, ".")
	if err = setupAppFile(workspace, vargs); err != nil {
		return err
	}
	if err = setupCronFile(workspace, vargs); err != nil {
		return err
	}
	err = runner.Run(vargs.AppCfgCmd, args...)
	if err != nil {
		return fmt.Errorf("error: %s\n", err)
	}
	return nil
}
type token struct {
	ProjectID string `json:"project_id"`
}
func getProjectFromToken(j string) string {
	t := token{}
	err := json.Unmarshal([]byte(j), &t)
	if err != nil {
		return ""
	}
	return t.ProjectID
}
func setupAppFile(workspace string, vargs GAE) error {
	return setupFile(workspace, vargs, "app.yaml", vargs.AppFile)
}