From ff12e548a87adc11bd084062ecae5cccce0280cd Mon Sep 17 00:00:00 2001 From: aryansharma9917 Date: Sat, 17 Jan 2026 16:21:08 +0530 Subject: [PATCH 1/9] add dry run mode to k8s apply logic --- pkg/k8s/apply.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pkg/k8s/apply.go b/pkg/k8s/apply.go index c850847..ceba068 100644 --- a/pkg/k8s/apply.go +++ b/pkg/k8s/apply.go @@ -52,7 +52,7 @@ func resolveContext(flag string) string { return "" } -func ApplyManifests(namespace, context string) error { +func ApplyManifests(namespace, context string, dryRun bool) error { path := filepath.Join("k8s", "app") if _, err := os.Stat(path); err != nil { @@ -72,10 +72,19 @@ func ApplyManifests(namespace, context string) error { args = append(args, "--context", context) } + if dryRun { + args = append(args, "--dry-run=client") + } + + if dryRun { + fmt.Println("dry run:", "kubectl", args) + return nil + } + cmd := exec.Command("kubectl", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - fmt.Println("running: kubectl", args) + fmt.Println("running:", "kubectl", args) return cmd.Run() } From 479232d58b5b93ae8e37a485714e12441ed04f7c Mon Sep 17 00:00:00 2001 From: aryansharma9917 Date: Sat, 17 Jan 2026 16:21:08 +0530 Subject: [PATCH 2/9] add dry run flag to k8s apply command --- cmd/k8s_apply.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/cmd/k8s_apply.go b/cmd/k8s_apply.go index fe3a339..51c3261 100644 --- a/cmd/k8s_apply.go +++ b/cmd/k8s_apply.go @@ -10,23 +10,26 @@ import ( var ( k8sNamespace string k8sContext string + k8sDryRun bool ) var k8sApplyCmd = &cobra.Command{ Use: "apply", Short: "Apply Kubernetes manifests to the current cluster", Run: func(cmd *cobra.Command, args []string) { - if err := k8s.CheckKubectl(); err != nil { - fmt.Println("info:", err.Error()) - return + // If not a dry run, check cluster connectivity + if !k8sDryRun { + if err := k8s.CheckKubectl(); err != nil { + fmt.Println("info:", err.Error()) + return + } + if err := k8s.CheckCluster(); err != nil { + fmt.Println("info:", err.Error()) + return + } } - if err := k8s.CheckCluster(); err != nil { - fmt.Println("info:", err.Error()) - return - } - - if err := k8s.ApplyManifests(k8sNamespace, k8sContext); err != nil { + if err := k8s.ApplyManifests(k8sNamespace, k8sContext, k8sDryRun); err != nil { fmt.Println("info:", err.Error()) return } @@ -36,6 +39,6 @@ var k8sApplyCmd = &cobra.Command{ func init() { k8sApplyCmd.Flags().StringVar(&k8sNamespace, "namespace", "", "Kubernetes namespace for deployment") k8sApplyCmd.Flags().StringVar(&k8sContext, "context", "", "Kubernetes context for deployment") - + k8sApplyCmd.Flags().BoolVar(&k8sDryRun, "dry-run", false, "Preview changes without applying them") k8sCmd.AddCommand(k8sApplyCmd) } From 9ae189ad650cf791e92d8347bda5891a58b09bba Mon Sep 17 00:00:00 2001 From: aryansharma9917 Date: Sat, 17 Jan 2026 16:26:56 +0530 Subject: [PATCH 3/9] add delete functionality for k8s manifests in logic layer --- pkg/k8s/delete.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 pkg/k8s/delete.go diff --git a/pkg/k8s/delete.go b/pkg/k8s/delete.go new file mode 100644 index 0000000..7276da7 --- /dev/null +++ b/pkg/k8s/delete.go @@ -0,0 +1,41 @@ +package k8s + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" +) + +func DeleteManifests(namespace, context string, dryRun bool) error { + path := filepath.Join("k8s", "app") + + if _, err := os.Stat(path); err != nil { + return fmt.Errorf("no manifests found at %s", path) + } + + namespace = resolveNamespace(namespace) + context = resolveContext(context) + + args := []string{"delete", "-f", path} + + if namespace != "" { + args = append(args, "-n", namespace) + } + + if context != "" { + args = append(args, "--context", context) + } + + if dryRun { + fmt.Println("dry run:", "kubectl", args) + return nil + } + + cmd := exec.Command("kubectl", args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + fmt.Println("running:", "kubectl", args) + return cmd.Run() +} From 8a6cdbf9e8497c66fd344187cf6172a26bf71218 Mon Sep 17 00:00:00 2001 From: aryansharma9917 Date: Sat, 17 Jan 2026 16:26:56 +0530 Subject: [PATCH 4/9] add delete command to k8s CLI with namespace, context and dry run flags --- cmd/k8s_delete.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 cmd/k8s_delete.go diff --git a/cmd/k8s_delete.go b/cmd/k8s_delete.go new file mode 100644 index 0000000..e650be5 --- /dev/null +++ b/cmd/k8s_delete.go @@ -0,0 +1,43 @@ +package cmd + +import ( + "fmt" + + "github.com/aryansharma9917/codewise-cli/pkg/k8s" + "github.com/spf13/cobra" +) + +var ( + k8sDeleteNamespace string + k8sDeleteContext string + k8sDeleteDryRun bool +) + +var k8sDeleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete Kubernetes resources from the current cluster", + Run: func(cmd *cobra.Command, args []string) { + if !k8sDeleteDryRun { + if err := k8s.CheckKubectl(); err != nil { + fmt.Println("info:", err.Error()) + return + } + if err := k8s.CheckCluster(); err != nil { + fmt.Println("info:", err.Error()) + return + } + } + + if err := k8s.DeleteManifests(k8sDeleteNamespace, k8sDeleteContext, k8sDeleteDryRun); err != nil { + fmt.Println("info:", err.Error()) + return + } + }, +} + +func init() { + k8sDeleteCmd.Flags().StringVar(&k8sDeleteNamespace, "namespace", "", "Kubernetes namespace for deletion") + k8sDeleteCmd.Flags().StringVar(&k8sDeleteContext, "context", "", "Kubernetes context for deletion") + k8sDeleteCmd.Flags().BoolVar(&k8sDeleteDryRun, "dry-run", false, "Preview deletion without applying") + k8sCmd.AddCommand(k8sDeleteCmd) +} From be5abed761b6803326aa15ab10e5e2b645e7d12c Mon Sep 17 00:00:00 2001 From: aryansharma9917 Date: Sat, 17 Jan 2026 16:31:48 +0530 Subject: [PATCH 5/9] add helm chart initialization logic for application packaging --- pkg/helm/init.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 pkg/helm/init.go diff --git a/pkg/helm/init.go b/pkg/helm/init.go new file mode 100644 index 0000000..52503c0 --- /dev/null +++ b/pkg/helm/init.go @@ -0,0 +1,90 @@ +package helm + +import ( + "fmt" + "os" + "path/filepath" +) + +func InitChart(appName, image string) error { + base := filepath.Join("helm", "chart") + + // Ensure base path + if _, err := os.Stat(base); err == nil { + return fmt.Errorf("helm chart already exists at %s", base) + } + + if err := os.MkdirAll(filepath.Join(base, "templates"), 0755); err != nil { + return err + } + + // Chart.yaml + chart := []byte(fmt.Sprintf(`apiVersion: v2 +name: %s +description: A Helm chart for deploying %s +version: 0.1.0 +appVersion: "1.0.0" +`, appName, appName)) + + if err := os.WriteFile(filepath.Join(base, "Chart.yaml"), chart, 0644); err != nil { + return err + } + + // values.yaml + values := []byte(fmt.Sprintf(`image: + repository: %s + tag: latest + pullPolicy: IfNotPresent + +namespace: default +`, image)) + + if err := os.WriteFile(filepath.Join(base, "values.yaml"), values, 0644); err != nil { + return err + } + + // templates + deployment := []byte(`apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} + namespace: {{ .Values.namespace }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }} + spec: + containers: + - name: app + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} +`) + + service := []byte(`apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} + namespace: {{ .Values.namespace }} +spec: + ports: + - port: 80 + targetPort: 80 + selector: + app: {{ .Release.Name }} +`) + + if err := os.WriteFile(filepath.Join(base, "templates", "deployment.yaml"), deployment, 0644); err != nil { + return err + } + + if err := os.WriteFile(filepath.Join(base, "templates", "service.yaml"), service, 0644); err != nil { + return err + } + + return nil +} From 9c762684843f4d34e5d642c4332bb21aee799aac Mon Sep 17 00:00:00 2001 From: aryansharma9917 Date: Sat, 17 Jan 2026 16:31:48 +0530 Subject: [PATCH 6/9] add helm init command to scaffold helm chart from config --- cmd/helm.go | 12 ++++++++++++ cmd/helm_init.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 cmd/helm.go create mode 100644 cmd/helm_init.go diff --git a/cmd/helm.go b/cmd/helm.go new file mode 100644 index 0000000..623b3e5 --- /dev/null +++ b/cmd/helm.go @@ -0,0 +1,12 @@ +package cmd + +import "github.com/spf13/cobra" + +var helmCmd = &cobra.Command{ + Use: "helm", + Short: "Helm chart tooling", +} + +func init() { + rootCmd.AddCommand(helmCmd) +} diff --git a/cmd/helm_init.go b/cmd/helm_init.go new file mode 100644 index 0000000..316ebac --- /dev/null +++ b/cmd/helm_init.go @@ -0,0 +1,49 @@ +package cmd + +import ( + "fmt" + + "github.com/aryansharma9917/codewise-cli/pkg/config" + "github.com/aryansharma9917/codewise-cli/pkg/helm" + "github.com/spf13/cobra" +) + +var ( + helmAppName string + helmImage string +) + +var helmInitCmd = &cobra.Command{ + Use: "init", + Short: "Initialize a Helm chart from Codewise configuration", + Run: func(cmd *cobra.Command, args []string) { + cfg, err := config.ReadConfig() + if err != nil { + fmt.Println("info: config not found, run `codewise config init` first") + return + } + + app := helmAppName + if app == "" { + app = cfg.Defaults.AppName + } + + image := helmImage + if image == "" { + image = cfg.Defaults.Image + } + + if err := helm.InitChart(app, image); err != nil { + fmt.Println("info:", err.Error()) + return + } + + fmt.Printf("helm chart created at helm/chart\n") + }, +} + +func init() { + helmInitCmd.Flags().StringVar(&helmAppName, "app", "", "Application name for chart") + helmInitCmd.Flags().StringVar(&helmImage, "image", "", "Container image for chart") + helmCmd.AddCommand(helmInitCmd) +} From d5e485e9df6be18022200de4a0dda60c0e3eddcb Mon Sep 17 00:00:00 2001 From: aryansharma9917 Date: Sat, 17 Jan 2026 16:32:19 +0530 Subject: [PATCH 7/9] add initial scaffolded helm chart for application deployment --- helm/chart/Chart.yaml | 5 +++++ helm/chart/templates/deployment.yaml | 19 +++++++++++++++++++ helm/chart/templates/service.yaml | 11 +++++++++++ helm/chart/values.yaml | 6 ++++++ 4 files changed, 41 insertions(+) create mode 100644 helm/chart/Chart.yaml create mode 100644 helm/chart/templates/deployment.yaml create mode 100644 helm/chart/templates/service.yaml create mode 100644 helm/chart/values.yaml diff --git a/helm/chart/Chart.yaml b/helm/chart/Chart.yaml new file mode 100644 index 0000000..3aac045 --- /dev/null +++ b/helm/chart/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: myapp +description: A Helm chart for deploying myapp +version: 0.1.0 +appVersion: "1.0.0" diff --git a/helm/chart/templates/deployment.yaml b/helm/chart/templates/deployment.yaml new file mode 100644 index 0000000..20cebec --- /dev/null +++ b/helm/chart/templates/deployment.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} + namespace: {{ .Values.namespace }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }} + spec: + containers: + - name: app + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} diff --git a/helm/chart/templates/service.yaml b/helm/chart/templates/service.yaml new file mode 100644 index 0000000..0a43194 --- /dev/null +++ b/helm/chart/templates/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} + namespace: {{ .Values.namespace }} +spec: + ports: + - port: 80 + targetPort: 80 + selector: + app: {{ .Release.Name }} diff --git a/helm/chart/values.yaml b/helm/chart/values.yaml new file mode 100644 index 0000000..1562bc3 --- /dev/null +++ b/helm/chart/values.yaml @@ -0,0 +1,6 @@ +image: + repository: + tag: latest + pullPolicy: IfNotPresent + +namespace: default From 818cc0f04927577c0e7ced1a499658ccef2dc359 Mon Sep 17 00:00:00 2001 From: aryansharma9917 Date: Sat, 17 Jan 2026 16:39:20 +0530 Subject: [PATCH 8/9] update README with new features and usage documentation --- README.md | 236 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 147 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 9b55173..f3de82e 100644 --- a/README.md +++ b/README.md @@ -1,176 +1,234 @@ -# codewise-cli +# Codewise CLI
- Codewise Logo + Codewise Logo
-Codewise is a powerful CLI tool that simplifies common DevOps tasks such as: +
- * Encoding/decoding files - * JSON/YAML conversions - * Dockerfile generation - * Kubernetes manifest scaffolding - * Rendering templates with Go's `text/template` engine ------ +Codewise is a DevOps-oriented command-line utility for scaffolding, packaging, and deploying containerized applications using a consistent workflow. It enables developers and platform engineers to move from source code to running workloads using Docker, Kubernetes, and Helm without switching tools or remembering boilerplate syntax. -## Getting Started: +--- -### Clone the Repository +## Features + +Codewise provides automation for: + +- Dockerfile scaffolding and image builds +- Kubernetes manifest generation and deployment +- Helm chart scaffolding +- Namespace, context, and dry-run support for Kubernetes operations +- Configuration bootstrapping for defaults (image, namespace, repo) +- File encoding utilities (Base64, YAML ⇄ JSON, ENV parsing) +- Templating & project bootstrap helpers + +--- + +## Installation + +Clone the repository: ```bash git clone https://github.com/aryansharma9917/codewise-cli.git cd codewise-cli -``` +```` -### Build the Binary +Build from source: ```bash go build -o codewise main.go ``` -### (Optional) Install Globally +(Optional) install globally: ```bash sudo mv codewise /usr/local/bin/ ``` ------ +--- ## Usage +General syntax: + +```bash +codewise [subcommand] [flags] +``` + +--- + +## Configuration + +Initialize a personal configuration: + ```bash -codewise [flags] +codewise config init +``` + +This creates: + +``` +~/.codewise/config.yaml ``` ------ +example: -## Commands & Examples +```yaml +defaults: + app_name: myapp + image: codewise:latest + namespace: default + context: "" +``` -### `encode` — Format Conversion & Encoding +Configuration values can be overridden with CLI flags. -| Conversion Type | Description | Example | -| :-------------- | :-------------------- | :-------------------------------------------------------------------------- | -| `JTY` | JSON to YAML | `codewise encode --input input.json --output output.yaml --type JTY` | -| `YTJ` | YAML to JSON | `codewise encode --input input.yaml --output output.json --type YTJ` | -| `KVTJ` | .env to JSON | `codewise encode --input .env --output env.json --type KVTJ` | -| `B64E` | Base64 Encode | `codewise encode --input input.txt --output encoded.txt --type B64E` | -| `B64D` | Base64 Decode | `codewise encode --input encoded.txt --output decoded.txt --type B64D` | +--- -### `generate` — Starter File Generators +## Docker Workflow -Generate a Dockerfile: +Initialize a Dockerfile: ```bash -codewise generate dockerfile --output Dockerfile +codewise docker init ``` -Generate a Kubernetes manifest: +Validate Dockerfile structure: ```bash -codewise generate k8s --output deployment.yaml +codewise docker validate ``` -### `template` — Render Templated YAMLs - -Use Go `.tpl` template and `.yaml` values file to generate output YAML: +Build container image: ```bash -codewise template --template template.tpl --values values.yaml --output rendered.yaml +codewise docker build ``` ------ +--- + +## Kubernetes Workflow -## 🐳 Docker Usage +Initialize manifests: -### 🔨 Build Docker Image +```bash +codewise k8s init +``` + +Apply manifests to a cluster: ```bash -docker build -t aryansharma04/codewise-cli:latest . +codewise k8s apply --namespace dev --context minikube ``` -### Run Using Docker +Dry-run mode (no cluster required): ```bash -docker run --rm -v $(pwd):/app aryansharma04/codewise-cli:latest +codewise k8s apply --dry-run ``` -Example: +Delete deployment: ```bash -docker run --rm -v $(pwd):/app aryansharma04/codewise-cli:latest encode --input /app/input.json --output /app/output.yaml --type JTY +codewise k8s delete --namespace dev ``` ------ +--- -## ✅ Running Tests +## Helm Workflow -Make sure Go is installed: +Scaffold a Helm chart: ```bash -go test ./... -v +codewise helm init ``` -Test coverage includes: +This creates: + +``` +helm/chart/ +├── Chart.yaml +├── values.yaml +└── templates/ + ├── deployment.yaml + └── service.yaml +``` - * ✅ JSON to YAML - * ✅ YAML to JSON - * ✅ ENV to JSON - * ✅ Base64 encode/decode - * ✅ Template rendering +--- ------ +## Encoding & Conversion Utilities -## 📁 Project Structure +Usage: ```bash -. -├── cmd/ # CLI command handlers -├── pkg/ -│ ├── encoder/ # Encoding & conversion logic -│ ├── generator/ # Project scaffolding logic -│ └── validator/ # Future: Schema & config validation -├── templates/ # Go template files -├── tests/ # Unit tests -├── testdata/ # Sample input files for testing -├── Dockerfile -├── Makefile -├── go.mod -├── main.go -└── README.md +codewise encode --input file --output out --type ``` ------ +Supported modes: + +| mode | description | +| ------ | ------------- | +| `JTY` | JSON → YAML | +| `YTJ` | YAML → JSON | +| `KVTJ` | .env → JSON | +| `B64E` | Base64 encode | +| `B64D` | Base64 decode | -## 🤝 Contributing +--- + +## Example End-to-End Workflow ```bash -# Fork and clone the repo -git clone https://github.com//codewise-cli.git -cd codewise-cli +codewise config init +codewise docker init +codewise docker build +codewise k8s init +codewise k8s apply --namespace dev --context minikube +codewise helm init +``` -# Create a feature branch -git checkout -b my-feature +--- -# Make changes and push -git add . -git commit -m "Add awesome feature" -git push origin my-feature +## Project Structure + +``` +. +├── cmd/ # CLI commands +├── pkg/ # Core logic (docker, k8s, helm, config, encode) +├── helm/ # Generated Helm charts +├── k8s/ # Generated Kubernetes manifests +├── config/ # Configuration helpers +├── Dockerfile +├── go.mod +└── main.go ``` -Then open a Pull Request 🚀 +--- + +## Roadmap + +Planned enhancements include: ------ +* Helm install/upgrade operations +* Helm push to OCI registries +* GitOps integration (ArgoCD / Flux) +* CI/CD pipeline generation (GitHub Actions) +* Image scanning (Trivy/Syft plugins) +* Terraform infrastructure modules +* Local cluster provisioning (kind/k3d/minikube) +* Plugin system for custom extensions +* Global binary installation via Homebrew/Scoop -## 🛡 License +--- -Licensed under the MIT License. See `LICENSE` for more. +## Contributing ------ +Contributions are welcome. The project follows a logical commit layering: -## 📚 Blog Post +* Logic changes under `pkg/` +* CLI wiring under `cmd/` +* Scaffold output under `k8s/` and `helm/` -Check out the behind-the-scenes story and development journey in Aryan's detailed blog post: +Fork, branch, commit, and open a PR. -> **📖 [I Built a DevOps CLI Tool in Go – Meet codewise-cli](https://dev.to/aryansharma9917/i-built-a-devops-cli-tool-in-go-meet-codewise-cli-5149)** -> Dive into how this tool was built from scratch, its motivations, challenges, features, and future roadmap. From dd7d7919dcecac580ff316cd36aab99838e52589 Mon Sep 17 00:00:00 2001 From: aryansharma9917 Date: Sat, 17 Jan 2026 16:54:02 +0530 Subject: [PATCH 9/9] refactor template files and add documentation comments for encoder tests --- templates/github-action.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/github-action.tpl b/templates/github-action.tpl index b786f93..1b091dd 100644 --- a/templates/github-action.tpl +++ b/templates/github-action.tpl @@ -1,3 +1,4 @@ +# cmd name: CI for {{.AppName}} on: