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 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.
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)
+}
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)
}
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)
+}
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
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
+}
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()
}
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()
+}
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: