Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ var (

var deployCmd = &cobra.Command{
Use: "deploy",
Short: "Deploy an application using the configured environment",
Short: "Deployment operations",
}

var deployRunCmd = &cobra.Command{
Use: "run",
Short: "Execute deployment",
Run: func(cmd *cobra.Command, args []string) {

if deployEnv == "" {
Expand All @@ -28,10 +33,31 @@ var deployCmd = &cobra.Command{
},
}

var deployPlanCmd = &cobra.Command{
Use: "plan",
Short: "Preview deployment execution plan",
Run: func(cmd *cobra.Command, args []string) {

if deployEnv == "" {
fmt.Println("please provide --env")
return
}

if err := deploy.Plan(deployEnv); err != nil {
fmt.Println("plan error:", err)
}
},
}

func init() {

deployCmd.Flags().StringVar(&deployEnv, "env", "", "Environment to deploy")
deployCmd.Flags().BoolVar(&dryRun, "dry-run", false, "Preview deployment")
deployRunCmd.Flags().StringVar(&deployEnv, "env", "", "Environment to deploy")
deployRunCmd.Flags().BoolVar(&dryRun, "dry-run", false, "Preview deployment")

deployPlanCmd.Flags().StringVar(&deployEnv, "env", "", "Environment to plan")

deployCmd.AddCommand(deployRunCmd)
deployCmd.AddCommand(deployPlanCmd)

rootCmd.AddCommand(deployCmd)
}
62 changes: 62 additions & 0 deletions pkg/deploy/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package deploy

import (
"fmt"

"github.com/aryansharma9917/codewise-cli/pkg/env"
)

type Command struct {
Name string
Args []string
}

func BuildCommand(environment *env.Env) (*Command, Strategy, error) {

strategy := ResolveStrategy(environment)

switch strategy {

case StrategyHelm:

args := []string{
"upgrade",
"--install",
environment.Helm.Release,
environment.Helm.Chart,
"--namespace",
environment.K8s.Namespace,
}

if environment.K8s.Context != "" {
args = append(args, "--kube-context", environment.K8s.Context)
}

return &Command{
Name: "helm",
Args: args,
}, strategy, nil

case StrategyKubectl:

args := []string{
"apply",
"-f",
"k8s/",
"-n",
environment.K8s.Namespace,
}

if environment.K8s.Context != "" {
args = append(args, "--context", environment.K8s.Context)
}

return &Command{
Name: "kubectl",
Args: args,
}, strategy, nil

default:
return nil, "", fmt.Errorf("unknown deployment strategy")
}
}
57 changes: 9 additions & 48 deletions pkg/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,57 +22,18 @@ func Run(envName string, dryRun bool) error {
return err
}

strategy := ResolveStrategy(environment)
command, _, err := BuildCommand(environment)
if err != nil {
return err
}

if err := checkDependency(command.Name); err != nil {
return err
}

executor := Executor{
DryRun: dryRun,
}

switch strategy {

case StrategyHelm:

if err := checkDependency("helm"); err != nil {
return err
}

args := []string{
"upgrade",
"--install",
environment.Helm.Release,
environment.Helm.Chart,
"--namespace",
environment.K8s.Namespace,
}

// inject kube-context if provided
if environment.K8s.Context != "" {
args = append(args, "--kube-context", environment.K8s.Context)
}

return executor.Run("helm", args...)

case StrategyKubectl:

if err := checkDependency("kubectl"); err != nil {
return err
}

args := []string{
"apply",
"-f",
"k8s/",
"-n",
environment.K8s.Namespace,
}

if environment.K8s.Context != "" {
args = append(args, "--context", environment.K8s.Context)
}

return executor.Run("kubectl", args...)

default:
return fmt.Errorf("unknown deployment strategy")
}
return executor.Run(command.Name, command.Args...)
}
7 changes: 5 additions & 2 deletions pkg/deploy/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import (
func LoadEnvironment(name string) (*env.Env, error) {

e, err := env.LoadEnv(name)
if err != nil {
return nil, fmt.Errorf("failed to load environment: %w", err)
if err != nil || e == nil {
return nil, fmt.Errorf(
"environment %q not found. run 'codewise env list' to see available environments",
name,
)
}

return e, nil
Expand Down
33 changes: 33 additions & 0 deletions pkg/deploy/plan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package deploy

import (
"fmt"
"strings"
)

func Plan(envName string) error {

environment, err := LoadEnvironment(envName)
if err != nil {
return err
}

command, strategy, err := BuildCommand(environment)
if err != nil {
return err
}

fmt.Println("\nDeployment Plan")
fmt.Println("---------------")

fmt.Println("Environment:", envName)
fmt.Println("Strategy:", strategy)

fmt.Println("\nCommand:")
fmt.Printf("%s %s\n\n",
command.Name,
strings.Join(command.Args, " "),
)

return nil
}
74 changes: 72 additions & 2 deletions pkg/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@ import (
"fmt"
"os"
"path/filepath"

"gopkg.in/yaml.v3"
)

const (
codewiseHomeEnv = "CODEWISE_HOME"
)

/////////////////////////////////////////////////////////
// PATH HELPERS
/////////////////////////////////////////////////////////

func baseEnvPath() (string, error) {
// Check override via env var

if home := os.Getenv(codewiseHomeEnv); home != "" {
return filepath.Join(home, "envs"), nil
}

// Fallback to ~/.codewise/envs
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("failed to resolve user home: %w", err)
Expand All @@ -26,26 +31,91 @@ func baseEnvPath() (string, error) {
}

func envDir(name string) (string, error) {

base, err := baseEnvPath()
if err != nil {
return "", err
}

return filepath.Join(base, name), nil
}

func envExists(name string) bool {

dir, err := envDir(name)
if err != nil {
return false
}

info, err := os.Stat(dir)
return err == nil && info.IsDir()
}

func ensureBaseDir() error {

base, err := baseEnvPath()
if err != nil {
return err
}

return os.MkdirAll(base, 0755)
}

/////////////////////////////////////////////////////////
// YAML READER
/////////////////////////////////////////////////////////

func readYAML(path string, out interface{}) error {

raw, err := os.ReadFile(path)
if err != nil {
return err
}

return yaml.Unmarshal(raw, out)
}

/////////////////////////////////////////////////////////
// SINGLE SOURCE OF TRUTH
/////////////////////////////////////////////////////////

func LoadEnv(name string) (*Env, error) {

if !envExists(name) {
return nil, fmt.Errorf("environment %q does not exist", name)
}

dir, err := envDir(name)
if err != nil {
return nil, err
}

k8s := K8sConfig{}
helm := HelmConfig{}
gitops := GitOpsConfig{}
values := ValuesConfig{}

if err := readYAML(filepath.Join(dir, "k8s.yaml"), &k8s); err != nil {
return nil, err
}

if err := readYAML(filepath.Join(dir, "helm.yaml"), &helm); err != nil {
return nil, err
}

if err := readYAML(filepath.Join(dir, "gitops.yaml"), &gitops); err != nil {
return nil, err
}

if err := readYAML(filepath.Join(dir, "values.yaml"), &values); err != nil {
return nil, err
}

return &Env{
Name: name,
K8s: k8s,
Helm: helm,
GitOps: gitops,
Values: values,
}, nil
}
Loading