Compare commits

...

8 Commits

Author SHA1 Message Date
0283ddf0d5
image 2021-12-01 14:36:35 +01:00
aee0deba4a
new ledo, with ecr login 2021-11-30 18:25:08 +01:00
46be715e9e
docker ECR login (go-aws-sdk) 2021-11-30 17:58:09 +01:00
1071695e87
code reformat, init fixes 2021-11-28 01:04:20 +01:00
da41b5820b
ledo init 2021-11-20 11:34:31 +01:00
8a91f699b8
check docker-compose version 2021-10-23 22:16:42 +02:00
b172d8f43d
Autocomplete 2021-10-21 18:17:51 +02:00
92133a6b2e
Fix commands.
Add username to config struct, if present execute `sudo -E -u username`
in command
2021-10-21 18:01:31 +02:00
53 changed files with 1318 additions and 113 deletions

2
.gitignore vendored
View File

@ -4,3 +4,5 @@ dist
.env .env
.ledo-mode .ledo-mode
main main
ledo
.ledo-back.yml

View File

@ -9,6 +9,14 @@ builds:
- linux - linux
- windows - windows
- darwin - darwin
goarch:
- "386"
- amd64
- arm
- arm64
goarm:
- "6"
- "7"
archives: archives:
- replacements: - replacements:
darwin: Darwin darwin: Darwin

View File

@ -8,15 +8,3 @@ docker:
modes: modes:
dev: docker/docker-compose.yml docker/docker-compose.dev.yml dev: docker/docker-compose.yml docker/docker-compose.dev.yml
launch: docker/docker-compose.yml launch: docker/docker-compose.yml
providers:
# you can mix and match many files
dotenv:
env_sync:
path: .env-dist
vault:
env_sync:
path: environment/data/billing/web/env
env:
SMTP_PASS:
path: environment/data/billing/global
field: SMTP

1
Dockerfile Normal file
View File

@ -0,0 +1 @@
FROM alpine

125
app/cmd/autocomplete.go Normal file
View File

@ -0,0 +1,125 @@
package cmd
import (
"fmt"
"io"
"net/http"
"os"
"os/exec"
"github.com/adrg/xdg"
"github.com/urfave/cli/v2"
)
// CmdAutocomplete manages autocompletion
var CmdAutocomplete = cli.Command{
Name: "shellcompletion",
Aliases: []string{"autocomplete"},
Category: catSetup,
Usage: "Install shell completion for ledo",
Description: "Install shell completion for ledo",
ArgsUsage: "<shell type> (bash, zsh, powershell, fish)",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "install",
Usage: "Persist in shell config instead of printing commands",
},
},
Action: runAutocompleteAdd,
}
func runAutocompleteAdd(ctx *cli.Context) error {
var remoteFile, localFile, cmds string
shell := ctx.Args().First()
switch shell {
case "zsh":
remoteFile = "contrib/autocomplete.zsh"
localFile = "autocomplete.zsh"
cmds = "echo 'PROG=ledo _CLI_ZSH_AUTOCOMPLETE_HACK=1 source \"%s\"' >> ~/.zshrc && source ~/.zshrc"
case "bash":
remoteFile = "contrib/autocomplete.sh"
localFile = "autocomplete.sh"
cmds = "echo 'PROG=ledo source \"%s\"' >> ~/.bashrc && source ~/.bashrc"
case "powershell":
remoteFile = "contrib/autocomplete.ps1"
localFile = "tea.ps1"
cmds = "\"& %s\" >> $profile"
case "fish":
return writeFishAutoCompleteFile(ctx)
default:
return fmt.Errorf("Must specify valid %s", ctx.Command.ArgsUsage)
}
localPath, err := xdg.ConfigFile("ledo/" + localFile)
if err != nil {
return err
}
cmds = fmt.Sprintf(cmds, localPath)
if err = writeRemoteAutoCompleteFile(remoteFile, localPath); err != nil {
return err
}
if ctx.Bool("install") {
fmt.Println("Installing in your shellrc")
installer := exec.Command(shell, "-c", cmds)
if shell == "powershell" {
installer = exec.Command("powershell.exe", "-Command", cmds)
}
out, err := installer.CombinedOutput()
if err != nil {
return fmt.Errorf("Couldn't run the commands: %s %s", err, out)
}
} else {
fmt.Println("\n# Run the following commands to install autocompletion (or use --install)")
fmt.Println(cmds)
}
return nil
}
func writeRemoteAutoCompleteFile(file, destPath string) error {
url := fmt.Sprintf("https://git.cynarski.pl/LeDo/ledo/raw/branch/master/%s", file)
fmt.Println("Fetching " + url)
res, err := http.Get(url)
if err != nil {
return err
}
defer res.Body.Close()
writer, err := os.Create(destPath)
if err != nil {
return err
}
defer writer.Close()
_, err = io.Copy(writer, res.Body)
return err
}
func writeFishAutoCompleteFile(ctx *cli.Context) error {
script, err := ctx.App.ToFishCompletion()
if err != nil {
return err
}
localPath, err := xdg.ConfigFile("fish/conf.d/tea_completion.fish")
if err != nil {
return err
}
writer, err := os.Create(localPath)
if err != nil {
return err
}
if _, err = io.WriteString(writer, script); err != nil {
return err
}
fmt.Printf("Installed tab completion to %s\n", localPath)
return nil
}

View File

@ -1,9 +1,8 @@
package cmd package cmd
import ( import (
"ledo/app/cmd/docker"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"ledo/app/cmd/docker"
) )
var CmdDocker = cli.Command{ var CmdDocker = cli.Command{
@ -12,10 +11,8 @@ var CmdDocker = cli.Command{
Category: catHelpers, Category: catHelpers,
Usage: "docker helper", Usage: "docker helper",
Description: `Manage docker-compose in project`, Description: `Manage docker-compose in project`,
Action: runDockerDefault,
Subcommands: []*cli.Command{ Subcommands: []*cli.Command{
&docker.CmdDockerPs, &docker.CmdDockerPs,
&docker.CmdDockerFqn,
&docker.CmdDockerUp, &docker.CmdDockerUp,
&docker.CmdComposeBuild, &docker.CmdComposeBuild,
&docker.CmdComposeDebug, &docker.CmdComposeDebug,
@ -28,10 +25,6 @@ var CmdDocker = cli.Command{
&docker.CmdComposeUpOnce, &docker.CmdComposeUpOnce,
&docker.CmdComposePull, &docker.CmdComposePull,
&docker.CmdComposeStop, &docker.CmdComposeStop,
&docker.CmdDockerLogin,
}, },
} }
func runDockerDefault(ctx *cli.Context) error {
return docker.RunComposePs(ctx)
}

View File

@ -7,7 +7,8 @@ import (
) )
var CmdComposeBuild = cli.Command{ var CmdComposeBuild = cli.Command{
Name: "pull", Name: "build",
Aliases: []string{"b"},
Usage: "build docker image", Usage: "build docker image",
Description: `Build all docker images`, Description: `Build all docker images`,
Action: RunComposeBuild, Action: RunComposeBuild,
@ -18,5 +19,3 @@ func RunComposeBuild(cmd *cli.Context) error {
compose.ExecComposerBuild(ctx) compose.ExecComposerBuild(ctx)
return nil return nil
} }

View File

@ -18,4 +18,3 @@ func RunComposeDebug(cmd *cli.Context) error {
compose.ExecComposerDebug(ctx) compose.ExecComposerDebug(ctx)
return nil return nil
} }

View File

@ -18,4 +18,3 @@ func RunComposeDown(cmd *cli.Context) error {
compose.ExecComposerDown(ctx) compose.ExecComposerDown(ctx)
return nil return nil
} }

32
app/cmd/docker/login.go Normal file
View File

@ -0,0 +1,32 @@
package docker
import (
"github.com/urfave/cli/v2"
"ledo/app/modules/context"
"ledo/app/modules/docker"
)
var CmdDockerLogin = cli.Command{
Name: "login",
Aliases: []string{"l"},
Usage: "Docker Registry login",
Description: `Login to docker registry`,
Subcommands: []*cli.Command{
&CmdDockerEcrLogin,
},
}
var CmdDockerEcrLogin = cli.Command{
Name: "ecr",
Aliases: []string{"e"},
Usage: "AWS Elastic Docker Registry",
Description: `Login to docker registry`,
Action: RunDockerEcrLogin,
}
func RunDockerEcrLogin(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
docker.DockerEcrLogin(ctx)
return nil
}

View File

@ -8,7 +8,7 @@ import (
var CmdComposeLogs = cli.Command{ var CmdComposeLogs = cli.Command{
Name: "logs", Name: "logs",
Aliases: []string{"l"}, Aliases: []string{"l"},
Usage: "logs from containers", Usage: "logs from containers",
Description: `Get fqn docker image defined as main service in config file`, Description: `Get fqn docker image defined as main service in config file`,
Action: RunComposeLogs, Action: RunComposeLogs,
@ -16,7 +16,6 @@ var CmdComposeLogs = cli.Command{
func RunComposeLogs(cmd *cli.Context) error { func RunComposeLogs(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
compose.ExecComposerLogs(ctx) compose.ExecComposerLogs(ctx, cmd.Args())
return nil return nil
} }

View File

@ -18,4 +18,3 @@ func RunComposePull(cmd *cli.Context) error {
compose.ExecComposerPull(ctx) compose.ExecComposerPull(ctx)
return nil return nil
} }

View File

@ -18,4 +18,3 @@ func RunComposeRestart(cmd *cli.Context) error {
compose.ExecComposerRestart(ctx) compose.ExecComposerRestart(ctx)
return nil return nil
} }

View File

@ -8,14 +8,18 @@ import (
var CmdComposeRun = cli.Command{ var CmdComposeRun = cli.Command{
Name: "run", Name: "run",
Aliases: []string{"r"},
Usage: "run cmd in main container", Usage: "run cmd in main container",
Description: `Run command in main container`, Description: `Run command in main container`,
ArgsUsage: "[<cmd>]",
Action: RunComposeRun, Action: RunComposeRun,
} }
func RunComposeRun(cmd *cli.Context) error { func RunComposeRun(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
compose.ExecComposerRun(ctx) if cmd.Args().Len() >= 1 {
compose.ExecComposerRun(ctx, cmd.Args())
return nil
}
return nil return nil
} }

View File

@ -8,6 +8,7 @@ import (
var CmdComposeShell = cli.Command{ var CmdComposeShell = cli.Command{
Name: "shell", Name: "shell",
Aliases: []string{"sh"},
Usage: "run shell from main service", Usage: "run shell from main service",
Description: `Execute shell cmd in main service`, Description: `Execute shell cmd in main service`,
Action: RunComposeShell, Action: RunComposeShell,
@ -18,4 +19,3 @@ func RunComposeShell(cmd *cli.Context) error {
compose.ExecComposerShell(ctx) compose.ExecComposerShell(ctx)
return nil return nil
} }

View File

@ -10,7 +10,7 @@ var CmdComposeStop = cli.Command{
Name: "stop", Name: "stop",
Usage: "stop containers", Usage: "stop containers",
Description: `Stop all containers defined in docker-compose stack mode`, Description: `Stop all containers defined in docker-compose stack mode`,
Action: RunComposePull, Action: RunComposeStop,
} }
func RunComposeStop(cmd *cli.Context) error { func RunComposeStop(cmd *cli.Context) error {
@ -18,5 +18,3 @@ func RunComposeStop(cmd *cli.Context) error {
compose.ExecComposerStop(ctx) compose.ExecComposerStop(ctx)
return nil return nil
} }

View File

@ -17,6 +17,6 @@ var CmdDockerUp = cli.Command{
func RunComposeUp(cmd *cli.Context) error { func RunComposeUp(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
compose.ExecComposerUp(ctx) compose.ExecComposerUp(ctx)
compose.ExecComposerLogs(ctx, cmd.Args())
return nil return nil
} }

View File

@ -18,4 +18,3 @@ func RunComposeUpOnce(cmd *cli.Context) error {
compose.ExecComposerUpOnce(ctx) compose.ExecComposerUpOnce(ctx)
return nil return nil
} }

23
app/cmd/image.go Normal file
View File

@ -0,0 +1,23 @@
package cmd
import (
"ledo/app/cmd/image"
"github.com/urfave/cli/v2"
)
var CmdImage = cli.Command{
Name: "image",
Aliases: []string{"i"},
Category: catHelpers,
Usage: "docker container helper",
Description: `docker container helper`,
Subcommands: []*cli.Command{
&image.CmdDockerFqn,
&image.CmdDockerPush,
&image.CmdDockerRetag,
&image.CmdDockerBuild,
},
}

45
app/cmd/image/build.go Normal file
View File

@ -0,0 +1,45 @@
package image
import (
"github.com/urfave/cli/v2"
"ledo/app/modules/context"
"ledo/app/modules/docker"
)
var CmdDockerBuild = cli.Command{
Name: "build",
Aliases: []string{"b"},
Usage: "build docker image",
Description: `Build docker image`,
ArgsUsage: "version",
Action: RunDockerBuild,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "stage",
Aliases: []string{"s"},
Value: "",
Usage: "select stage to build (multi-stage dockerfile)",
Required: false,
},
&cli.StringFlag{
Name: "dockerfile",
Aliases: []string{"f"},
Value: "./Dockerfile",
Usage: "select dockerfile",
Required: false,
},
&cli.StringFlag{
Name: "opts",
Aliases: []string{"o"},
Value: "--compress",
Usage: "Additional build options",
Required: false,
},
},
}
func RunDockerBuild(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
docker.ExecDockerBuild(ctx, cmd.Args(), *cmd)
return nil
}

View File

@ -1,9 +1,9 @@
package docker package image
import ( import (
"fmt" "fmt"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"ledo/app/modules/compose" "ledo/app/modules/docker"
"ledo/app/modules/context" "ledo/app/modules/context"
) )
@ -17,6 +17,6 @@ var CmdDockerFqn = cli.Command{
func RunDockerFqn(cmd *cli.Context) error { func RunDockerFqn(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
fmt.Printf("%v", compose.ShowDockerImageFQN(ctx)) fmt.Printf("%v", docker.ShowDockerImageFQN(ctx))
return nil return nil
} }

22
app/cmd/image/push.go Normal file
View File

@ -0,0 +1,22 @@
package image
import (
"github.com/urfave/cli/v2"
"ledo/app/modules/context"
"ledo/app/modules/docker"
)
var CmdDockerPush = cli.Command{
Name: "push",
Aliases: []string{"p"},
Usage: "push docker to registry",
Description: `Push docker image to docker registry`,
ArgsUsage: "version",
Action: RunDockerPush,
}
func RunDockerPush(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
docker.ExecDockerPush(ctx, cmd.Args())
return nil
}

22
app/cmd/image/retag.go Normal file
View File

@ -0,0 +1,22 @@
package image
import (
"github.com/urfave/cli/v2"
"ledo/app/modules/context"
"ledo/app/modules/docker"
)
var CmdDockerRetag = cli.Command{
Name: "retag",
Aliases: []string{"r"},
Usage: "retag docker image",
Description: `Change docker image tag`,
ArgsUsage: "fromTag toTag",
Action: RunDockerRetag,
}
func RunDockerRetag(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
docker.ExecDockerRetag(ctx, cmd.Args())
return nil
}

109
app/cmd/init.go Normal file
View File

@ -0,0 +1,109 @@
package cmd
import (
"github.com/urfave/cli/v2"
"html/template"
"ledo/app/helper"
"ledo/app/modules/compose"
"ledo/app/modules/context"
"ledo/app/modules/dockerfile"
"ledo/app/modules/interact"
"ledo/app/templates"
"log"
"os"
)
var CmdInit = cli.Command{
Name: "init",
Category: catSetup,
Usage: "Init ledo in project",
Description: `Initialize LeadDocker in current project`,
Action: runInitLedo,
}
func runInitLedo(cmd *cli.Context) error {
var advRun bool
config, err := context.LoadConfigFile()
if err != nil {
return err
}
data, err := interact.InitLedoProject(config.Docker)
if err != nil {
return err
}
tpl, err := template.New("tpl").Parse(templates.LedoConfigurationFileTemplate)
if err != nil {
return err
}
if err != nil {
log.Fatalln(err)
}
f, err := os.Create("./.ledo.yml")
if err != nil {
return err
}
err = tpl.Execute(f, data)
if err != nil {
return err
}
advRun = false
// advRun := interact.InitAdvancedConfigurationAsk("Run advanced docker mode configuration?")
if advRun == true {
ctx := context.InitCommand(cmd)
dConf, _ := interact.InitDocker()
err = dockerfile.CreateDockerFile(dConf)
if err != nil {
return err
}
projectComposeConfig := helper.DockerProjectCfg{}
projectComposeConfig.DockerBaseImage = dConf.DockerBaseImage
var dockerComposeServices []helper.DockerProjectAdditionalServiceCfg
var dockerComposeModeConfig []helper.DockerComposeModeCfg
for _, composeMode := range interact.PredefinedDockerComposeModes {
var configMode bool
if composeMode == "base" {
configMode = true
} else {
configMode = interact.InitAdvancedConfigurationAsk("Configure "+composeMode+" stack?")
}
if configMode == true {
for {
serviceCfg, _ := interact.CreateDockerService()
serviceCfg.DockerServiceMode = composeMode
dockerComposeServices = append(dockerComposeServices, serviceCfg)
addAnother := interact.InitAdvancedConfigurationAsk("Add another service do " + composeMode + " stack?")
if addAnother == false {
break
}
}
composeFilename := "./docker/docker-compose.yml"
if composeMode != "base" {
composeFilename = "./docker/docker-compose."+composeMode+".yml"
}
mdCfg := helper.DockerComposeModeCfg{
DockerComposeName: composeFilename,
DockerComposeServices: dockerComposeServices,
}
dockerComposeModeConfig = append(dockerComposeModeConfig, mdCfg)
}
}
projectComposeConfig.DockerComposeModes = dockerComposeModeConfig
err = compose.CreateComposeFile(ctx, projectComposeConfig, "base")
if err != nil {
return err
}
}
return err
}

View File

@ -21,4 +21,3 @@ var CmdMode = cli.Command{
func runModeDefault(cmd *cli.Context) error { func runModeDefault(cmd *cli.Context) error {
return mode.RunSelectMode(cmd) return mode.RunSelectMode(cmd)
} }

View File

@ -17,6 +17,3 @@ func RunModeList(cmd *cli.Context) error {
ctx.Mode.PrintListModes() ctx.Mode.PrintListModes()
return nil return nil
} }

View File

@ -20,8 +20,7 @@ func RunSelectMode(cmd *cli.Context) error {
ctx.Mode.SetMode(cmd.Args().First()) ctx.Mode.SetMode(cmd.Args().First())
return nil return nil
} }
// interact.SelectDockerHubTag("paramah/php")
interact.SelectMode(ctx, "") interact.SelectMode(ctx, "")
return nil return nil
} }

View File

@ -0,0 +1,77 @@
package helper
type DockerServiceEnvironment struct {
EnvironmentName string `yaml:"environment_name,omitempty" json:"environmentName"`
EnvironmentValue string `yaml:"environment_value,omitempty" json:"environmentValue"`
}
type DockerProjectAdditionalServiceCfg struct {
DockerServiceMode string `yaml:"docker_service_mode,omitempty" json:"dockerServiceMode"`
DockerServiceType string `yaml:"docker_service_type,omitempty" json:"dockerServiceType"`
DockerServiceImage string `yaml:"docker_service_image,omitempty" json:"dockerServiceImage"`
DockerServiceTag string `yaml:"docker_service_tag,omitempty" json:"dockerServiceTag"`
DockerServiceEnvironment []DockerServiceEnvironment `yaml:"docker_service_environment,omitempty" json:"dockerServiceEnvironment"`
}
type DockerComposeModeCfg struct {
DockerComposeName string `yaml:"docker_compose_name,omitempty" json:"dockerComposeName"`
DockerComposeServices []DockerProjectAdditionalServiceCfg `yaml:"docker_compose_services,omitempty" json:"dockerComposeServices"`
}
type DockerProjectCfg struct {
HasDockerRegistry bool `yaml:"has_docker_registry,omitempty" json:"hasDockerRegistry"`
DockerBaseImage string `yaml:"docker_base_image,omitempty" json:"dockerBaseImage"`
DockerBaseTag string `yaml:"docker_base_tag,omitempty" json:"dockerBaseTag"`
DockerComposeModes []DockerComposeModeCfg `yaml:"docker_compose_modes,omitempty" json:"dockerComposeModes"`
}
//var PredefinedAdditionalService = []DockerProjectAdditionalServiceCfg{
// {
// DockerServiceMode: "base",
// DockerServiceType: "database",
// DockerServiceImage: "mariadb",
// DockerServiceTag: "",
// DockerServiceEnvironment: []DockerServiceEnvironment{
// {
// EnvironmentName: "DB_NAME",
// EnvironmentValue: "dev",
// },
// },
// },
// {
// DockerServiceMode: "dev",
// DockerServiceType: "tools",
// DockerServiceImage: "dbgate/dbgate",
// DockerServiceTag: "",
// },
// {
// DockerServiceMode: "dev",
// DockerServiceType: "tools",
// DockerServiceImage: "mailhog/mailhog",
// DockerServiceTag: "",
// },
// {
// DockerServiceMode: "dev",
// DockerServiceType: "tools",
// DockerServiceImage: "minio/minio",
// DockerServiceTag: "",
// },
// {
// DockerServiceMode: "base",
// DockerServiceType: "security",
// DockerServiceImage: "authelia/authelia",
// DockerServiceTag: "",
// },
// {
// DockerServiceMode: "base",
// DockerServiceType: "infrastructure",
// DockerServiceImage: "traefik",
// DockerServiceTag: "",
// },
// {
// DockerServiceMode: "base",
// DockerServiceType: "infrastructure",
// DockerServiceImage: "portainer",
// DockerServiceTag: "",
// },
//}

View File

@ -0,0 +1,36 @@
package aws_ledo
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"os"
)
func getRegion() string {
region, exists := os.LookupEnv("AWS_REGION")
if !exists {
fmt.Printf("AWS_REGION not set and unable to fetch region")
os.Exit(1)
}
return region
}
func EcrLogin() (*ecr.GetAuthorizationTokenOutput, error) {
var input *ecr.GetAuthorizationTokenInput
var token *ecr.GetAuthorizationTokenOutput
config := &aws.Config{
Region: aws.String(getRegion()),
}
sess, _ := session.NewSession(config)
_, err := sess.Config.Credentials.Get()
if err != nil {
return token, err
}
ecrCtx := ecr.New(sess)
token, err = ecrCtx.GetAuthorizationToken(input)
return token, nil
}

View File

@ -3,8 +3,8 @@ package compose
import ( import (
"bytes" "bytes"
"errors" "errors"
"fmt"
"github.com/Masterminds/semver" "github.com/Masterminds/semver"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"io/ioutil" "io/ioutil"
"ledo/app/modules/context" "ledo/app/modules/context"
@ -14,9 +14,10 @@ import (
"strings" "strings"
) )
const DockerComposeVersion = ">= 1.28.6" const DockerComposeVersion = ">= 1.28.0"
func CheckDockerComposeVersion() { func CheckDockerComposeVersion() {
// cmd := exec.Command("docker-compose", "--version")
cmd := exec.Command("docker-compose", "--version") cmd := exec.Command("docker-compose", "--version")
var output bytes.Buffer var output bytes.Buffer
cmd.Stdout = &output cmd.Stdout = &output
@ -34,50 +35,46 @@ func CheckDockerComposeVersion() {
composeSemVer, _ := semver.NewVersion(composeVersion) composeSemVer, _ := semver.NewVersion(composeVersion)
if !verConstraint.Check(composeSemVer) { if !verConstraint.Check(composeSemVer) {
log.Fatal("Wrong docker-compose version, please update to 1.28.6 or higher.") log.Fatal("Wrong docker-compose version, please update to "+DockerComposeVersion+" or higher.")
} }
} }
func MergeComposerFiles(filenames ...string) (string, error) { func MergeComposerFiles(filenames ...string) (string, error) {
var resultValues map[string]interface{} var resultValues map[string]interface{}
if len(filenames) <= 0 { if len(filenames) <= 0 {
return "", errors.New("You must provide at least one filename for reading Values") return "", errors.New("You must provide at least one filename for reading Values")
} }
for _, filename := range filenames { for _, filename := range filenames {
var override map[string]interface{} var override map[string]interface{}
bs, err := ioutil.ReadFile(filename) bs, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
continue continue
} }
if err := yaml.Unmarshal(bs, &override); err != nil { if err := yaml.Unmarshal(bs, &override); err != nil {
log.Print(err) log.Print(err)
continue continue
} }
if resultValues == nil { if resultValues == nil {
resultValues = override resultValues = override
} else { } else {
for k, v := range override { for k, v := range override {
resultValues[k] = v resultValues[k] = v
} }
} }
} }
bs, err := yaml.Marshal(resultValues) bs, err := yaml.Marshal(resultValues)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
return "", err return "", err
} }
return string(bs), nil return string(bs), nil
}
func ShowDockerImageFQN(ctx *context.LedoContext) string {
return fmt.Sprintf("%s/%s/%s/master:latest", ctx.Config.Docker.Registry, ctx.Config.Docker.Namespace, ctx.Config.Docker.Name)
} }
func ExecComposerUp(ctx *context.LedoContext) { func ExecComposerUp(ctx *context.LedoContext) {
@ -104,7 +101,6 @@ func ExecComposerBuild(ctx *context.LedoContext) {
ctx.ExecCmd("docker-compose", args[0:]) ctx.ExecCmd("docker-compose", args[0:])
} }
func ExecComposerDown(ctx *context.LedoContext) { func ExecComposerDown(ctx *context.LedoContext) {
args := ctx.ComposeArgs args := ctx.ComposeArgs
args = append(args, "down") args = append(args, "down")
@ -123,9 +119,10 @@ func ExecComposerRestart(ctx *context.LedoContext) {
ctx.ExecCmd("docker-compose", args[0:]) ctx.ExecCmd("docker-compose", args[0:])
} }
func ExecComposerLogs(ctx *context.LedoContext) { func ExecComposerLogs(ctx *context.LedoContext, command cli.Args) {
args := ctx.ComposeArgs args := ctx.ComposeArgs
args = append(args, "logs", "--follow", "--tail", "100") args = append(args, "logs", "--follow", "--tail", "100")
args = append(args, command.Slice()...)
ctx.ExecCmd("docker-compose", args[0:]) ctx.ExecCmd("docker-compose", args[0:])
} }
@ -147,9 +144,13 @@ func ExecComposerDebug(ctx *context.LedoContext) {
ctx.ExecCmd("docker-compose", args[0:]) ctx.ExecCmd("docker-compose", args[0:])
} }
func ExecComposerRun(ctx *context.LedoContext) { func ExecComposerRun(ctx *context.LedoContext, command cli.Args) {
args := ctx.ComposeArgs args := ctx.ComposeArgs
args = append(args, "run", strings.ToLower(ctx.Config.Docker.MainService), ctx.Config.Docker.Shell) args = append(args, "run", strings.ToLower(ctx.Config.Docker.MainService))
if ctx.Config.Docker.Username != "" {
args = append(args, "sudo", "-E", "-u", ctx.Config.Docker.Username)
}
args = append(args, command.Slice()...)
ctx.ExecCmd("docker-compose", args[0:]) ctx.ExecCmd("docker-compose", args[0:])
} }
@ -158,7 +159,3 @@ func ExecComposerUpOnce(ctx *context.LedoContext) {
args = append(args, "up", "--force-recreate", "--renew-anon-volumes", "--abort-on-container-exit", "--exit-code-from", ctx.Config.Docker.MainService) args = append(args, "up", "--force-recreate", "--renew-anon-volumes", "--abort-on-container-exit", "--exit-code-from", ctx.Config.Docker.MainService)
ctx.ExecCmd("docker-compose", args[0:]) ctx.ExecCmd("docker-compose", args[0:])
} }

View File

@ -0,0 +1,42 @@
package compose
import (
"html/template"
"ledo/app/helper"
"ledo/app/modules/context"
"ledo/app/templates"
"log"
"os"
)
func CreateComposeFile(ctx *context.LedoContext, dockerProject helper.DockerProjectCfg, composeMode string) error {
if _, err := os.Stat("./docker"); os.IsNotExist(err) {
err := os.Mkdir("./docker", 0755)
if err != nil {
log.Fatal(err)
}
}
log.Printf("%v", dockerProject)
templateName := templates.LedoDockerComposeBaseFileTemplate_base
tpl, err := template.New("dockercompose").Parse(templateName)
if err != nil {
log.Fatalln(err)
}
composeFilename := "./docker/docker-compose.yml"
if composeMode != "base" {
composeFilename = "./docker/docker-compose."+composeMode+".yml"
}
f, err := os.Create(composeFilename)
if err != nil {
log.Println("create file: ", err)
}
err = tpl.Execute(f, ctx)
return err
}

View File

@ -6,9 +6,9 @@ import (
) )
type LedoFile struct { type LedoFile struct {
Docker DockerMap `yaml:"docker,omitempty"` Docker DockerMap `yaml:"docker,omitempty"`
Modes map[string]string `yaml:"modes,omitempty"` Modes map[string]string `yaml:"modes,omitempty"`
Project string `yaml:"project,omitempty"` Project string `yaml:"project,omitempty"`
} }
type DockerMap struct { type DockerMap struct {
@ -17,6 +17,7 @@ type DockerMap struct {
Name string `yaml:"name,omitempty"` Name string `yaml:"name,omitempty"`
MainService string `yaml:"main_service,omitempty"` MainService string `yaml:"main_service,omitempty"`
Shell string `yaml:"shell,omitempty"` Shell string `yaml:"shell,omitempty"`
Username string `yaml:"username,omitempty"`
} }
func NewLedoFile(s string) (*LedoFile, error) { func NewLedoFile(s string) (*LedoFile, error) {

View File

@ -12,16 +12,16 @@ import (
type LedoContext struct { type LedoContext struct {
*cli.Context *cli.Context
Config *config.LedoFile Config *config.LedoFile
ComposeArgs []string ComposeArgs []string
Mode mode.Mode Mode mode.Mode
Output string Output string
} }
func InitCommand(ctx *cli.Context) *LedoContext { func InitCommand(ctx *cli.Context) *LedoContext {
var ( var (
c LedoContext c LedoContext
cfg *config.LedoFile cfg *config.LedoFile
) )
@ -34,8 +34,13 @@ func InitCommand(ctx *cli.Context) *LedoContext {
modeYml = ".jz-mode" modeYml = ".jz-mode"
} }
mode := mode.InitMode(modeYml, configYml) if _, err := os.Stat(configYml); err != nil {
c.Mode = mode fmt.Printf("Config file not found. Please run ledo init\n")
os.Exit(1)
}
ledoMode := mode.InitMode(modeYml, configYml)
c.Mode = ledoMode
c.Output = ctx.String("output") c.Output = ctx.String("output")
@ -45,10 +50,10 @@ func InitCommand(ctx *cli.Context) *LedoContext {
args := []string{"--env-file", ".env"} args := []string{"--env-file", ".env"}
args = append(args, "--project-name", strings.ToLower(strings.Replace(c.Config.Docker.Name, "/", "-", -1))) args = append(args, "--project-name", strings.ToLower(strings.Replace(c.Config.Docker.Name, "/", "-", -1)))
composes, _ := mode.GetModeConfig() composes, _ := ledoMode.GetModeConfig()
for _, element := range composes { for _, element := range composes {
args = append(args, "-f") args = append(args, "-f")
args = append(args, element) args = append(args, element)
} }
c.ComposeArgs = args c.ComposeArgs = args
@ -56,6 +61,18 @@ func InitCommand(ctx *cli.Context) *LedoContext {
return &c return &c
} }
func LoadConfigFile() (*config.LedoFile, error) {
configYml := ".ledo.yml"
if _, err := os.Stat(configYml); err != nil {
nilCfg := &config.LedoFile{}
return nilCfg, err
}
cfg, _ := config.NewLedoFile(configYml)
return cfg, nil
}
func (lx *LedoContext) ExecCmd(cmd string, cmdArgs []string) error { func (lx *LedoContext) ExecCmd(cmd string, cmdArgs []string) error {
fmt.Printf("Execute: %v %v\n", cmd, strings.Join(cmdArgs, " ")) fmt.Printf("Execute: %v %v\n", cmd, strings.Join(cmdArgs, " "))
command := exec.Command(cmd, cmdArgs...) command := exec.Command(cmd, cmdArgs...)
@ -64,3 +81,11 @@ func (lx *LedoContext) ExecCmd(cmd string, cmdArgs []string) error {
command.Stderr = os.Stderr command.Stderr = os.Stderr
return command.Run() return command.Run()
} }
func (lx *LedoContext) ExecCmdSilent(cmd string, cmdArgs []string) error {
command := exec.Command(cmd, cmdArgs...)
command.Stdin = os.Stdin
command.Stdout = os.Stdout
command.Stderr = nil
return command.Run()
}

View File

@ -0,0 +1,95 @@
package docker
import (
b64 "encoding/base64"
"fmt"
"github.com/urfave/cli/v2"
"ledo/app/modules/aws_ledo"
"ledo/app/modules/context"
"net/url"
"os"
"strings"
)
func trimLeftChars(s string, n int) string {
m := 0
for i := range s {
if m >= n {
return s[i:]
}
m++
}
return s[:0]
}
func DockerEcrLogin(ctx *context.LedoContext) error {
ecr, err := aws_ledo.EcrLogin()
if err != nil {
fmt.Println("Ecr login error: %s", err)
os.Exit(1)
}
password := *ecr.AuthorizationData[0].AuthorizationToken
ecrUrl := *ecr.AuthorizationData[0].ProxyEndpoint
sDec, _ := b64.StdEncoding.DecodeString(password)
registryAddr, err := url.Parse(ecrUrl)
if err != nil {
fmt.Printf("Ecr endpoint addr parse error: %s", err)
os.Exit(1)
}
ctx.ExecCmdSilent("docker", []string{"login", "-u", "AWS", "-p", string(trimLeftChars(string(sDec), 4)), registryAddr.Host})
return nil
}
func ShowDockerImageFQN(ctx *context.LedoContext) string {
fqn := fmt.Sprintf("%s/%s/%s", ctx.Config.Docker.Registry, ctx.Config.Docker.Namespace, ctx.Config.Docker.Name)
return strings.ToLower(fqn)
}
func ExecDockerPush(ctx *context.LedoContext, command cli.Args) {
var version string
var args []string
if command.First() == "" {
version = "latest"
} else {
version = command.First()
}
image := ShowDockerImageFQN(ctx)
args = append(args, "push")
args = append(args, "-f", image + ":" + version)
ctx.ExecCmd("docker", args[0:])
}
func ExecDockerRetag(ctx *context.LedoContext, command cli.Args) {
var args []string
from := command.Get(0)
to := command.Get(1)
image := ShowDockerImageFQN(ctx)
args = append(args, "tag")
args = append(args, image + ":" + from)
args = append(args, image + ":" + to)
ctx.ExecCmd("docker", args[0:])
}
func ExecDockerBuild(ctx *context.LedoContext, command cli.Args, cmdCtx cli.Context) {
var version string
var args []string
if command.First() == "" {
version = "latest"
} else {
version = command.First()
}
opts := strings.Split(strings.Trim(cmdCtx.String("opts"), " "), " ")
image := ShowDockerImageFQN(ctx)
args = append(args, "build")
args = append(args, "-t", image+":"+version)
args = append(args, "-f", cmdCtx.String("dockerfile"))
args = append(args, opts...)
if cmdCtx.String("stage") != "" {
args = append(args, "--target", cmdCtx.String("stage"))
}
args = append(args, ".")
ctx.ExecCmd("docker", args[0:])
}

View File

@ -0,0 +1,57 @@
package docker_hub
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
type DockerSearchResponse struct {
NumPages int `json:"num_pages"`
NumResults int `json:"num_results"`
PageSize int `json:"page_size"`
Page int `json:"page"`
Query string `json:"query"`
Results []DockerImage `json:"results"`
}
type DockerImage struct {
Name string `json:"name"`
Description string `json:"description"`
StarCount int `json:"star_count"`
IsTrusted bool `json:"is_trusted"`
IsAutomated bool `json:"is_automated"`
IsOfficial bool `json:"is_official"`
}
var urlSearch = "https://registry.hub.docker.com/v1/search"
func GetImage(image string) []DockerImage {
var resp DockerSearchResponse
searchString := url.QueryEscape(image)
urlSearch = urlSearch + "/?q=" + searchString
fmt.Printf("%v", urlSearch)
res, _ := http.Get(urlSearch)
defer res.Body.Close()
bytes, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err.Error())
}
json.Unmarshal(bytes, &resp)
return resp.Results
}
func DockerImageToArray(images []DockerImage) []string {
n := len(images)
arrTags := make([]string, n)
for idx, image := range images {
arrTags[idx] = image.Name
}
return arrTags
}

View File

@ -0,0 +1,40 @@
package docker_hub
import (
"encoding/json"
"io/ioutil"
"net/http"
)
type DockerImageTag struct {
Layer string `json:"layer"`
Name string `json:"name"`
}
var urlTags = "https://registry.hub.docker.com/v1/repositories"
func GetImageTags(image string) []DockerImageTag {
urlTags = urlTags + "/" + image + "/tags"
res, _ := http.Get(urlTags)
defer res.Body.Close()
bytes, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err.Error())
}
var tags []DockerImageTag
json.Unmarshal(bytes, &tags)
return tags
}
func ImageTagsToArray(tags []DockerImageTag) []string {
n := len(tags)
arrTags := make([]string, n)
for idx, tag := range tags {
arrTags[idx] = tag.Name
}
return arrTags
}

View File

@ -0,0 +1,33 @@
package dockerfile
import (
"html/template"
"ledo/app/helper"
"ledo/app/templates"
"log"
"os"
)
func CreateDockerFile(cfg helper.DockerProjectCfg) error {
if _, err := os.Stat("./Dockerfile"); err == nil {
log.Printf("Dockerfile exists!")
os.Exit(1)
}
templateName := templates.DockerFileTemplate_default
if cfg.DockerBaseImage == "paramah/php" {
templateName = templates.DockerFileTemplate_php
}
tpl, err := template.New("dockerfile").Parse(templateName)
if err != nil {
log.Fatalln(err)
}
f, err := os.Create("./Dockerfile")
if err != nil {
log.Println("create file: ", err)
}
err = tpl.Execute(f, cfg)
return err
}

View File

@ -0,0 +1,64 @@
package interact
import (
"github.com/AlecAivazis/survey/v2"
"ledo/app/modules/docker_hub"
)
func SelectDockerHubTag(dockerImage string) (string, error) {
dockerImageTags := docker_hub.GetImageTags(dockerImage)
selectedTag := "latest"
var qs = []*survey.Question{
{
Name: "tags",
Prompt: &survey.Select{
Message: "Select available docker image tag",
PageSize: 10,
Options: docker_hub.ImageTagsToArray(dockerImageTags),
},
},
}
err := survey.Ask(qs, &selectedTag)
if err != nil {
return "", err
}
return selectedTag, err
}
func EnterDockerImage() (string, error) {
dockerImage := ""
prompt := &survey.Input{
Message: "Enter docker base image: ",
Help: "This is base Dockerfile image (FROM image)",
}
survey.AskOne(prompt, &dockerImage)
return dockerImage, nil
}
func SearchDockerImage(image string) (string, error) {
dockerImages := docker_hub.GetImage(image)
selectedImage := ""
var qs = []*survey.Question{
{
Name: "image",
Prompt: &survey.Select{
Message: "Select available docker image",
PageSize: 100,
Options: docker_hub.DockerImageToArray(dockerImages),
},
},
}
err := survey.Ask(qs, &selectedImage)
if err != nil {
return "", err
}
return selectedImage, err
}

View File

@ -0,0 +1,24 @@
package interact
import (
"ledo/app/helper"
)
func InitDocker() (helper.DockerProjectCfg, error) {
dockerConfig := helper.DockerProjectCfg{}
image, err := EnterDockerImage()
if err != nil {
return dockerConfig, err
}
tag, err := SelectDockerHubTag(image)
if err != nil {
return dockerConfig, err
}
dockerConfig.DockerBaseImage = image
dockerConfig.DockerBaseTag = tag
return dockerConfig, err
}

View File

@ -0,0 +1,51 @@
package interact
import (
"github.com/AlecAivazis/survey/v2"
"ledo/app/helper"
)
var PredefinedDockerComposeModes = []string{
"base",
"dev",
"test",
}
func CreateDockerService() (helper.DockerProjectAdditionalServiceCfg, error) {
dockerServiceConfig := helper.DockerProjectAdditionalServiceCfg{}
var qs = []*survey.Question{
//{
// Name: "DockerServiceType",
// Prompt: &survey.Select{
// Message: "Select type of service",
// PageSize: 10,
// Options: []string{"database", "development", "tools"},
// },
//},
//{
// Name: "DockerServiceMode",
// Prompt: &survey.Select{
// Message: "Select docker-compose mode (file)",
// PageSize: 10,
// Options: PredefinedDockerComposeModes,
// },
//},
{
Name: "DockerServiceImage",
Prompt: &survey.Input{Message: "Enter image name: "},
Validate: survey.Required,
Transform: survey.ToLower,
},
}
err := survey.Ask(qs, &dockerServiceConfig)
if err != nil {
return dockerServiceConfig, err
}
return dockerServiceConfig, err
}

View File

@ -0,0 +1,74 @@
package interact
import (
"github.com/AlecAivazis/survey/v2"
"ledo/app/modules/config"
)
func InitLedoProject(dockerConfig config.DockerMap) (config.DockerMap, error) {
if dockerConfig.Registry == "" {
dockerConfig.Registry = ""
}
if dockerConfig.Shell == "" {
dockerConfig.Shell = "/bin/bash"
}
if dockerConfig.Username == "" {
dockerConfig.Username = "root"
}
var qs = []*survey.Question{
{
Name: "Registry",
Prompt: &survey.Input{Message: "Enter docker registry address: ", Default: dockerConfig.Registry, Help: "Docker registry for main service image"},
Validate: survey.Required,
Transform: survey.ToLower,
},
{
Name: "Namespace",
Prompt: &survey.Input{Message: "Enter project namespace: ", Default: dockerConfig.Namespace, Help: "Project namespace (eq. GitLab project group)"},
Validate: survey.Required,
Transform: survey.ToLower,
},
{
Name: "Name",
Prompt: &survey.Input{Message: "Enter project name: ", Default: dockerConfig.Name},
Validate: survey.Required,
Transform: survey.ToLower,
},
{
Name: "MainService",
Prompt: &survey.Input{Message: "Enter docker-compose main service name: ", Default: dockerConfig.MainService, Help: "Main service, important if you want use ledo shell command or ledo run command"},
Validate: survey.Required,
Transform: survey.ToLower,
},
{
Name: "Shell",
Prompt: &survey.Input{Message: "Enter default shell: ", Default: dockerConfig.Shell},
Validate: survey.Required,
Transform: survey.ToLower,
},
{
Name: "Username",
Prompt: &survey.Input{Message: "Enter docker main service username: ", Default: dockerConfig.Username, Help: "Default user, if set ledo run command was execute with sudo user"},
Validate: survey.Required,
Transform: survey.ToLower,
},
}
err := survey.Ask(qs, &dockerConfig)
if err != nil {
return config.DockerMap{}, err
}
return dockerConfig, err
}
func InitAdvancedConfigurationAsk(question string) bool {
runAdv := false
prompt := &survey.Confirm{Message: question}
survey.AskOne(prompt, &runAdv)
return runAdv
}

View File

@ -1,7 +1,7 @@
package interact package interact
import ( import (
survey "github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/thoas/go-funk" "github.com/thoas/go-funk"
"ledo/app/modules/context" "ledo/app/modules/context"
) )

View File

@ -58,7 +58,7 @@ func (c *Mode) GetMode() string {
func (c *Mode) PrintListModes() { func (c *Mode) PrintListModes() {
fmt.Printf("Available modes:\n") fmt.Printf("Available modes:\n")
for m, _ := range c.availableModes { for m := range c.availableModes {
fmt.Printf("- %v\n", m) fmt.Printf("- %v\n", m)
} }
} }
@ -96,14 +96,12 @@ func (c *Mode) SetMode(modeName string) (bool, error) {
} }
func (c *Mode) GetModeConfig() ([]string, error) { func (c *Mode) GetModeConfig() ([]string, error) {
_, err := c.CheckMode(c.CurrentMode); _, err := c.CheckMode(c.CurrentMode)
if err == nil { if err == nil {
composes := strings.Fields(c.availableModes[c.CurrentMode]) composes := strings.Fields(c.availableModes[c.CurrentMode])
return composes, nil return composes, nil
} }
return nil, errors.New("Selected mode not exists in config file") return nil, errors.New("Selected mode not exists in config file")
} }

View File

@ -0,0 +1,15 @@
package templates
var LedoDockerComposeBaseFileTemplate_base = `
version: '3.7'
services:
{{.Config.Docker.MainService}}:
image: {{.Config.Docker.Registry}}
networks:
- network-{{.Config.Docker.Namespace}}
env_file: ${PWD}/.env
networks:
nestork-{{.Config.Docker.Namespace}}: {}
`

View File

@ -0,0 +1,52 @@
package templates
var DockerFileTemplate_default = `
FROM {{.DockerBaseImage}}:{{.DockerBaseTag}}
ENV DIR /usr/local
WORKDIR ${DIR}
# Copy entrypoint
COPY docker/docker-entrypoint.sh /bin/docker-entrypoint.sh
# Copy project content
COPY ./app $DIR
ENTRYPOINT ["docker-entrypoint.sh"]
CMD [""]
`
var DockerFileTemplate_php = `
FROM {{.DockerBaseImage}}:{{.DockerBaseTag}}
ARG ENVIRONMENT=production
RUN ngxconfig sf.conf
ENV DIR /var/www
WORKDIR ${DIR}
# Copy entrypoint
COPY docker/docker-entrypoint.sh /bin/docker-entrypoint.sh
RUN chmod +x /bin/docker-entrypoint.sh
# Develop packages
RUN xdebug_enable
RUN usermod -u 1000 www-data && groupmod -g 1000 www-data
RUN chown www-data:www-data ${DIR} && /bin/composer self-update --2
USER www-data
# For Docker build cache
COPY ./composer.* $DIR/
RUN /bin/composer install --no-scripts --no-interaction --no-autoloader && composer clear-cache
# Copy application
COPY --chown=www-data:www-data ./ $DIR
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 80
# done
USER root
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]
`

View File

@ -0,0 +1,31 @@
package templates
var DockerEntrypointTemplate_bash = `
#!/bin/bash
set -eo pipefail
shopt -s nullglob
h1() {
declare border padding text
border='\e[1;34m'"$(printf '=%.0s' $(seq 1 "$term_width"))"'\e[0m'
padding="$(printf ' %.0s' $(seq 1 $(((term_width - $(wc -m <<<"$*")) / 2))))"
text="\\e[1m$*\\e[0m"
echo -e "$border"
echo -e "${padding}${text}${padding}"
echo -e "$border"
}
h2() {
printf '\e[1;33m==>\e[37;1m %s\e[0m\n' "$*"
}
h1 "Project init"
###
### Your code here
###
h1 "End of init"
exec "$@"
`

View File

@ -0,0 +1,17 @@
package templates
var LedoConfigurationFileTemplate = `
docker:
registry: {{.Registry}}
namespace: {{.Namespace}}
name: {{.Name}}
main_service: {{.MainService}}
shell: {{.Shell}}
{{- if ne .Username "root" }}
username: {{.Username}}
{{end}}
modes:
base: docker/docker-compose.yml
dev: docker/docker-compose.yml docker/docker-compose.dev.yml
test: docker/docker-compose.yml docker/docker-compose.test.yml
`

9
contrib/autocomplete.ps1 Normal file
View File

@ -0,0 +1,9 @@
$fn = $($MyInvocation.MyCommand.Name)
$name = $fn -replace "(.*)\.ps1$", '$1'
Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock {
param($commandName, $wordToComplete, $cursorPosition)
$other = "$wordToComplete --generate-bash-completion"
Invoke-Expression $other | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}

21
contrib/autocomplete.sh Normal file
View File

@ -0,0 +1,21 @@
#! /bin/bash
: ${PROG:=$(basename ${BASH_SOURCE})}
_cli_bash_autocomplete() {
if [[ "${COMP_WORDS[0]}" != "source" ]]; then
local cur opts base
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ "$cur" == "-"* ]]; then
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion )
else
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
}
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG
unset PROG

23
contrib/autocomplete.zsh Normal file
View File

@ -0,0 +1,23 @@
#compdef $PROG
_cli_zsh_autocomplete() {
local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
else
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}")
fi
if [[ "${opts[1]}" != "" ]]; then
_describe 'values' opts
else
_files
fi
return
}
compdef _cli_zsh_autocomplete $PROG

9
go.mod
View File

@ -5,6 +5,9 @@ go 1.17
require ( require (
github.com/AlecAivazis/survey/v2 v2.3.2 github.com/AlecAivazis/survey/v2 v2.3.2
github.com/Masterminds/semver v1.5.0 github.com/Masterminds/semver v1.5.0
github.com/adrg/xdg v0.3.4
github.com/aws/aws-sdk-go v1.42.15
github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49
github.com/spf13/viper v1.8.1 github.com/spf13/viper v1.8.1
github.com/thoas/go-funk v0.9.1 github.com/thoas/go-funk v0.9.1
github.com/urfave/cli/v2 v2.3.0 github.com/urfave/cli/v2 v2.3.0
@ -15,7 +18,10 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kr/binarydist v0.1.0 // indirect
github.com/kr/pretty v0.2.0 // indirect github.com/kr/pretty v0.2.0 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect github.com/magiconair/properties v1.8.5 // indirect
@ -33,8 +39,9 @@ require (
github.com/subosito/gotenv v1.2.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b // indirect golang.org/x/sys v0.0.0-20210902050250-f475640dd07b // indirect
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect
golang.org/x/text v0.3.5 // indirect golang.org/x/text v0.3.6 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/inconshreveable/go-update.v0 v0.0.0-20150814200126-d8b0b1d421aa // indirect
gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
) )

22
go.sum
View File

@ -45,10 +45,14 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/adrg/xdg v0.3.4 h1:0BivHfQ0LSGQrFTaEZ0hyQLm/HAidci7m+1cT6wKKdA=
github.com/adrg/xdg v0.3.4/go.mod h1:61xAR2VZcggl2St4O9ohF5qCKe08+JDmE4VNzPFQvOQ=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.42.15 h1:RcUChuF7KzrrTqx9LAzJbLBX00LkUY7cH9T1VdxNdqk=
github.com/aws/aws-sdk-go v1.42.15/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -172,15 +176,23 @@ github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDG
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/binarydist v0.1.0 h1:6kAoLA9FMMnNGSehX0s1PdjbEaACznAv/W219j2uvyo=
github.com/kr/binarydist v0.1.0/go.mod h1:DY7S//GCoz1BCd0B0EVrinCKAZN3pXe+MDaIZbXQVgM=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
@ -218,6 +230,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -228,6 +241,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49 h1:LuxslTBxJrrNeKfqoywIERWWhH43TgiVAiPEVlhgNBA=
github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49/go.mod h1:fY313ZGG810aWruFYcyq3coFpHDrWJVoMfSRI81y1r4=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@ -357,6 +372,7 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -421,6 +437,7 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b h1:S7hKs0Flbq0bbc9xgYt4stIEG1zNDFqyrPwAX2Wj/sE= golang.org/x/sys v0.0.0-20210902050250-f475640dd07b h1:S7hKs0Flbq0bbc9xgYt4stIEG1zNDFqyrPwAX2Wj/sE=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -433,8 +450,9 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -600,6 +618,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/go-update.v0 v0.0.0-20150814200126-d8b0b1d421aa h1:drvf2JoUL1fz3ttkGNkw+rf3kZa2//7XkYGpSO4NHNA=
gopkg.in/inconshreveable/go-update.v0 v0.0.0-20150814200126-d8b0b1d421aa/go.mod h1:tuNm0ntQ7IH9VSA39XxzLMpee5c2DwgIbjD4x3ydo8Y=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

38
main.go
View File

@ -2,8 +2,11 @@ package main
import ( import (
"fmt" "fmt"
"github.com/sanbornm/go-selfupdate/selfupdate"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"ledo/app/cmd" "ledo/app/cmd"
"ledo/app/modules/compose"
"log"
"os" "os"
) )
@ -11,16 +14,25 @@ var (
version = "dev" version = "dev"
) )
func GetCurrentVersion() string {
return version
}
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
compose.CheckDockerComposeVersion()
app.Name = "ledo" app.Name = "ledo"
app.Usage = "LeadDocker helper for docker-compose work" app.Usage = "LeadDocker helper for docker-compose work"
app.Description = appDescription app.Description = appDescription
app.CustomAppHelpTemplate = helpTemplate app.CustomAppHelpTemplate = helpTemplate
app.Version = version app.Version = GetCurrentVersion()
app.Commands = []*cli.Command{ app.Commands = []*cli.Command{
&cmd.CmdInit,
&cmd.CmdDocker, &cmd.CmdDocker,
&cmd.CmdImage,
&cmd.CmdMode, &cmd.CmdMode,
&cmd.CmdAutocomplete,
// &CmdSelfupdate,
} }
app.EnableBashCompletion = true app.EnableBashCompletion = true
err := app.Run(os.Args) err := app.Run(os.Args)
@ -73,3 +85,27 @@ var helpTemplate = bold(`
func bold(t string) string { func bold(t string) string {
return fmt.Sprintf("\033[1m%s\033[0m", t) return fmt.Sprintf("\033[1m%s\033[0m", t)
} }
var CmdSelfupdate = cli.Command{
Name: "selfupdate",
Aliases: []string{"update"},
Category: "SETUP",
Usage: "Self update Ledo",
Action: runSelfupdate,
}
var updater = &selfupdate.Updater{
CurrentVersion: GetCurrentVersion(),
ApiURL: "http://updates.yourdomain.com/",
BinURL: "http://updates.yourdomain.com/",
DiffURL: "http://updates.yourdomain.com/",
Dir: "update/",
CmdName: "ledo",
}
func runSelfupdate(ctx *cli.Context) error {
log.Printf("check and update Ledo binary")
updater.BackgroundRun()
return nil
}