diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8a7c7e1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,69 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.25' + + - name: Check formatting + run: | + unformatted=$(gofmt -l .) + if [ -n "$unformatted" ]; then + echo "Unformatted files detected:"; echo "$unformatted"; exit 1 + fi + + - name: Run go vet + run: go vet ./... + + - name: Install golangci-lint + run: | + mkdir -p ./bin + GOBIN=$(pwd)/bin go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + + - name: Run golangci-lint (verbose) and save log + run: | + set +e + mkdir -p artifacts + ./bin/golangci-lint run ./... --verbose > artifacts/golangci.log 2>&1 || true + rc=$? + cat artifacts/golangci.log + echo $rc > artifacts/golangci_exit + + - name: Upload golangci-lint log + uses: actions/upload-artifact@v4 + with: + name: golangci-log + path: artifacts/golangci.log + + - name: Fail if golangci-lint reported issues + run: | + rc=$(cat artifacts/golangci_exit || echo 0) + if [ "$rc" != "0" ]; then + echo "golangci-lint failed with exit code $rc" + exit $rc + fi + + - name: Tidy modules + run: go mod tidy + + - name: Build + run: go build -v -o codewise main.go + + - name: Test + run: go test ./... -v diff --git a/Dockerfile b/Dockerfile index 4f38aba..e76bed1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Stage 1: Build the Go binary -FROM golang:1.22-alpine AS builder +FROM golang:1.25-alpine AS builder # Set the working directory WORKDIR /app @@ -15,7 +15,7 @@ COPY . . RUN go build -o codewise main.go # Stage 2: Minimal runtime image -FROM alpine:latest +FROM alpine:3.22 # Create a non-root user for better security (optional) RUN adduser -D appuser diff --git a/PROJECT_OVERVIEW.md b/PROJECT_OVERVIEW.md index 7600bd9..f78a74b 100644 --- a/PROJECT_OVERVIEW.md +++ b/PROJECT_OVERVIEW.md @@ -80,7 +80,7 @@ Terminal User ## Tech Stack Primary: -- Go 1.20 +- Go 1.22 - Cobra CLI framework (`github.com/spf13/cobra`) - YAML support (`gopkg.in/yaml.v3`) - TOML support (`github.com/BurntSushi/toml`) diff --git a/cmd/docker.go b/cmd/docker.go index ed51528..23227e5 100644 --- a/cmd/docker.go +++ b/cmd/docker.go @@ -40,7 +40,7 @@ var dockerBuildCmd = &cobra.Command{ Short: "Build Docker image", RunE: func(cmd *cobra.Command, args []string) error { if err := docker.BuildDockerImage(imageTag); err != nil { - return LogError("Docker build failed: %v", err) + return LogErrorf("Docker build failed: %v", err) } LogSuccess("Docker image built successfully") return nil diff --git a/cmd/env_create.go b/cmd/env_create.go index e267612..e9f62c4 100644 --- a/cmd/env_create.go +++ b/cmd/env_create.go @@ -20,14 +20,14 @@ var envCreateCmd = &cobra.Command{ if interactive { if err := createEnvInteractive(name); err != nil { - return LogError("error: %v", err) + return LogErrorf("error: %v", err) } LogSuccess("environment %s created", name) return nil } if err := env.CreateEnv(name, env.CreateOptions{}); err != nil { - return LogError("error: %v", err) + return LogErrorf("error: %v", err) } LogSuccess("environment %s created", name) return nil diff --git a/cmd/env_delete.go b/cmd/env_delete.go index 1679386..932f08f 100644 --- a/cmd/env_delete.go +++ b/cmd/env_delete.go @@ -23,7 +23,7 @@ var envDeleteCmd = &cobra.Command{ Message: fmt.Sprintf("Delete environment %q?", name), } if err := survey.AskOne(prompt, &confirm); err != nil { - return LogError("error: %v", err) + return LogErrorf("error: %v", err) } if !confirm { return LogError("aborted") @@ -31,7 +31,7 @@ var envDeleteCmd = &cobra.Command{ } if err := env.DeleteEnv(name, env.DeleteOptions{Force: yes}); err != nil { - return LogError("error: %v", err) + return LogErrorf("error: %v", err) } LogSuccess("environment %s deleted", name) diff --git a/cmd/errors.go b/cmd/errors.go index 0dfd947..5729d53 100644 --- a/cmd/errors.go +++ b/cmd/errors.go @@ -1,21 +1,28 @@ package cmd import ( + "errors" "fmt" "os" ) // ExitError prints an error message and exits with status code 1. -func ExitError(format string, args ...interface{}) error { - fmt.Fprintf(os.Stderr, "error: %s\n", fmt.Sprintf(format, args...)) - return fmt.Errorf(format, args...) +func ExitError(message string) error { + fmt.Fprintf(os.Stderr, "error: %s\n", message) + return errors.New(message) } // LogError prints an info/warning message but does not exit. -func LogError(format string, args ...interface{}) error { +func LogError(message string) error { + fmt.Fprintf(os.Stderr, "info: %s\n", message) + return errors.New(message) +} + +// LogErrorf prints a formatted info/warning message but does not exit. +func LogErrorf(format string, args ...interface{}) error { msg := fmt.Sprintf(format, args...) fmt.Fprintf(os.Stderr, "info: %s\n", msg) - return fmt.Errorf(msg) + return errors.New(msg) } // LogInfo prints an info message to stderr. diff --git a/cmd/template.go b/cmd/template.go index 54bdad4..878ca70 100644 --- a/cmd/template.go +++ b/cmd/template.go @@ -19,24 +19,30 @@ var templateCmd = &cobra.Command{ var githubActionCmd = &cobra.Command{ Use: "github-action", Short: "Generate a GitHub Actions CI workflow", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { data := generator.TemplateData{ AppName: appName, Repo: repoURL, } - generator.RenderTemplate("github-action", outputPath, data) + if err := generator.RenderTemplate("github-action", outputPath, data); err != nil { + return ExitError(err.Error()) + } + return nil }, } var argoAppCmd = &cobra.Command{ Use: "argo-app", Short: "Generate an ArgoCD application manifest", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { data := generator.TemplateData{ AppName: appName, Repo: repoURL, } - generator.RenderTemplate("argo-app", outputPath, data) + if err := generator.RenderTemplate("argo-app", outputPath, data); err != nil { + return ExitError(err.Error()) + } + return nil }, } @@ -52,6 +58,8 @@ func init() { cmd.Flags().StringVarP(&outputPath, "output", "o", "", "Output file path (required)") cmd.Flags().StringVar(&appName, "app-name", "myapp", "Application name for template") cmd.Flags().StringVar(&repoURL, "repo", "https://github.com/example/repo", "Repository URL for template") - cmd.MarkFlagRequired("output") + if err := cmd.MarkFlagRequired("output"); err != nil { + panic(err) + } } } diff --git a/go.mod b/go.mod index 650b24e..d1b2cd7 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/aryansharma9917/codewise-cli -go 1.20 +go 1.25.0 require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/clbanning/mxj/v2 v2.7.0 - github.com/spf13/cobra v1.6.1 + github.com/spf13/cobra v1.10.2 github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e ) @@ -13,28 +13,27 @@ replace github.com/aryansharma9917/codewise-cli => ./ require ( github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/mattn/go-colorable v0.1.2 // indirect - github.com/mattn/go-isatty v0.0.8 // indirect - github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.22 // indirect + github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect ) require ( - github.com/BurntSushi/toml v1.5.0 + github.com/BurntSushi/toml v1.6.0 github.com/kr/pretty v0.3.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) require ( - github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github v17.0.0+incompatible // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.6.0 // indirect + github.com/google/go-querystring v1.2.0 // indirect + github.com/hashicorp/go-version v1.9.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + golang.org/x/net v0.55.0 // indirect gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index e3c917d..791639a 100644 --- a/go.sum +++ b/go.sum @@ -1,31 +1,30 @@ github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= +github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= +github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA= +github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -35,35 +34,40 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 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/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e h1:IWllFTiDjjLIf2oeKxpIUmtiDV5sn71VgeQgg6vcE7k= github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e/go.mod h1:d7u6HkTYKSv5m6MCKkOQlHwaShTMl3HjqSGW3XtVhXM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -72,23 +76,22 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/docker/docker.go b/pkg/docker/docker.go index 9538b4e..a99ad85 100644 --- a/pkg/docker/docker.go +++ b/pkg/docker/docker.go @@ -17,14 +17,14 @@ func InitDockerfile() error { } defaultDockerfile := []byte(` -FROM golang:1.21-alpine AS builder +FROM golang:1.25-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app -FROM alpine:latest +FROM alpine:3.22 WORKDIR /root/ COPY --from=builder /app/app . EXPOSE 8080 @@ -63,6 +63,10 @@ func ValidateDockerfile() error { } } + if err := scanner.Err(); err != nil { + return fmt.Errorf("failed to read Dockerfile: %w", err) + } + fmt.Println("Dockerfile validation:") fmt.Println("----------------------") fmt.Println("Base image:", baseImage) diff --git a/pkg/docker/docker_test.go b/pkg/docker/docker_test.go new file mode 100644 index 0000000..05cddd2 --- /dev/null +++ b/pkg/docker/docker_test.go @@ -0,0 +1,58 @@ +package docker + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func withTempWorkingDir(t *testing.T) string { + t.Helper() + + dir := t.TempDir() + oldwd, err := os.Getwd() + if err != nil { + t.Fatalf("get working directory: %v", err) + } + + if err := os.Chdir(dir); err != nil { + t.Fatalf("chdir to temp dir: %v", err) + } + + t.Cleanup(func() { + _ = os.Chdir(oldwd) + }) + + return dir +} + +func TestInitDockerfileCreatesUpdatedGoBaseImage(t *testing.T) { + withTempWorkingDir(t) + + if err := InitDockerfile(); err != nil { + t.Fatalf("InitDockerfile() error = %v", err) + } + + content, err := os.ReadFile(filepath.Join(".", dockerfileName)) + if err != nil { + t.Fatalf("ReadFile() error = %v", err) + } + + text := string(content) + if !strings.Contains(text, "FROM golang:1.25-alpine AS builder") { + t.Fatalf("generated Dockerfile did not use Go 1.25: %s", text) + } +} + +func TestValidateDockerfileAcceptsGeneratedDockerfile(t *testing.T) { + withTempWorkingDir(t) + + if err := InitDockerfile(); err != nil { + t.Fatalf("InitDockerfile() error = %v", err) + } + + if err := ValidateDockerfile(); err != nil { + t.Fatalf("ValidateDockerfile() error = %v", err) + } +} diff --git a/pkg/generator/project.go b/pkg/generator/project.go index 89fecb3..714dd82 100644 --- a/pkg/generator/project.go +++ b/pkg/generator/project.go @@ -30,7 +30,7 @@ func ScaffoldProject(projectName string, withDocker, withDeployment bool) { // Dockerfile if withDocker { - dockerfile := `FROM golang:1.21 + dockerfile := `FROM golang:1.25-alpine WORKDIR /app COPY . . RUN go build -o main . diff --git a/pkg/prompt/prompt.go b/pkg/prompt/prompt.go index ce164f6..22ba28a 100644 --- a/pkg/prompt/prompt.go +++ b/pkg/prompt/prompt.go @@ -13,7 +13,9 @@ func AskInputFile() string { prompt := &survey.Input{ Message: "Enter the input file path:", } - survey.AskOne(prompt, &path) + if err := survey.AskOne(prompt, &path); err != nil { + return "" + } return path } @@ -23,7 +25,9 @@ func AskOutputFile() string { prompt := &survey.Input{ Message: "Enter the output file path:", } - survey.AskOne(prompt, &path) + if err := survey.AskOne(prompt, &path); err != nil { + return "" + } return path } @@ -35,6 +39,8 @@ func ConfirmOverwrite(file string) bool { var confirm bool message := fmt.Sprintf("File %s already exists. Overwrite?", file) - survey.AskOne(&survey.Confirm{Message: message}, &confirm) + if err := survey.AskOne(&survey.Confirm{Message: message}, &confirm); err != nil { + return false + } return confirm } diff --git a/templates/github-action.tpl b/templates/github-action.tpl index 1b091dd..64ad910 100644 --- a/templates/github-action.tpl +++ b/templates/github-action.tpl @@ -13,6 +13,6 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version: '1.22' - name: Build run: go build -v ./...