k8s-deploy-helper 0.1.1 β†’ 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1 +1,176 @@
1
- ## K8s-deploy-helper
1
+ # k8s-deploy-helper
2
+
3
+ A production-grade CLI tool that automates Docker image builds and Kubernetes deployments using a single command.
4
+ Designed for real-world CI/CD pipelines and local developer workflows.
5
+
6
+ ---
7
+
8
+ ## ✨ Overview
9
+
10
+ `k8s-deploy-helper` simplifies the process of:
11
+ - Building Docker images
12
+ - Pushing images to container registries
13
+ - Generating Kubernetes manifests
14
+ - Deploying applications to Kubernetes clusters
15
+
16
+ The tool is **configuration-driven**, **CI/CD-friendly**, and uses **native Docker and kubectl** instead of opaque SDK abstractions.
17
+
18
+ ---
19
+
20
+ ## πŸš€ Features
21
+
22
+ - Docker image build automation
23
+ - Registry push support (Docker Hub, ECR, GHCR, etc.)
24
+ - Kubernetes Deployment & Service YAML generation
25
+ - Automatic `kubectl apply`
26
+ - Graceful handling of partial failures
27
+ - Idempotent deployments
28
+ - Works locally and in CI pipelines
29
+
30
+ ---
31
+
32
+ ## πŸ“¦ Installation
33
+
34
+ Install globally from npm:
35
+ npm install -g k8s-deploy-helper
36
+
37
+ Verify installation:
38
+ k8s-deploy --version
39
+
40
+ πŸ›  Prerequisites
41
+ Ensure the following are installed and configured:
42
+
43
+ Node.js (v18+)
44
+
45
+ Docker (running)
46
+
47
+ kubectl
48
+
49
+ Kubernetes cluster:
50
+
51
+ Docker Desktop Kubernetes
52
+
53
+ Minikube
54
+
55
+ EKS / GKE / AKS
56
+
57
+ Container registry access (Docker Hub, ECR, GHCR, etc.)
58
+
59
+ πŸ“„ Configuration
60
+ Create a k8s.config.json file in your project root.
61
+
62
+ Example configuration:
63
+ json
64
+ Copy code
65
+ {
66
+ "appName": "my-app",
67
+ "namespace": "default",
68
+
69
+ "docker": {
70
+ "image": "username/my-app",
71
+ "tag": "latest",
72
+ "context": ".",
73
+ "dockerfile": "Dockerfile"
74
+ },
75
+
76
+ "kubernetes": {
77
+ "replicas": 2,
78
+ "containerPort": 3000,
79
+ "servicePort": 80,
80
+ "serviceType": "ClusterIP"
81
+ },
82
+
83
+ "resources": {
84
+ "limits": {
85
+ "cpu": "500m",
86
+ "memory": "512Mi"
87
+ },
88
+ "requests": {
89
+ "cpu": "250m",
90
+ "memory": "256Mi"
91
+ }
92
+ },
93
+
94
+ "env": {
95
+ "NODE_ENV": "production"
96
+ }
97
+ }
98
+ ▢️ Usage
99
+ Deploy your application using a single command:
100
+
101
+ bash
102
+ Copy code
103
+ k8s-deploy deploy
104
+ What this command does:
105
+ Builds the Docker image
106
+
107
+ Pushes the image to the registry (if authorized)
108
+
109
+ Generates Kubernetes manifests:
110
+
111
+ deployment.yaml
112
+
113
+ service.yaml
114
+
115
+ Applies the resources to the Kubernetes cluster using kubectl
116
+
117
+ If the image push fails, deployment continues using the existing image tag.
118
+
119
+ πŸ“ Generated Files
120
+ text
121
+ Copy code
122
+ deployment.yaml
123
+ service.yaml
124
+ These files can be reviewed, committed to version control, or applied independently.
125
+
126
+ πŸ” Authentication
127
+ This tool does not manage credentials.
128
+
129
+ Authentication must be handled externally:
130
+
131
+ docker login for container registries
132
+
133
+ kubectl config for Kubernetes clusters
134
+
135
+ This mirrors real-world CI/CD environments and avoids secret leakage.
136
+
137
+ 🧠 Design Principles
138
+ Explicit over implicit β€” no hidden magic
139
+
140
+ Fail loud, fail early β€” CI-friendly logs
141
+
142
+ Idempotent operations β€” safe re-runs
143
+
144
+ No vendor lock-in β€” relies on standard tools
145
+
146
+ πŸ§ͺ Example Output
147
+ text
148
+ Copy code
149
+ 🐳 Building Docker image: username/my-app:latest
150
+ πŸ“€ Pushing Docker image: username/my-app:latest
151
+ ⚠️ Image push failed β€” continuing deployment
152
+
153
+ ☸️ Deploying to Kubernetes namespace: default
154
+ πŸ“¦ Applying deployment.yaml
155
+ deployment.apps/my-app created
156
+ πŸ“¦ Applying service.yaml
157
+ service/my-app created
158
+ βœ… Kubernetes resources applied successfully
159
+ πŸ—Ί Roadmap
160
+ Planned enhancements:
161
+
162
+ --dry-run mode
163
+
164
+ Rollback support
165
+
166
+ Helm chart generation
167
+
168
+ Deployment health checks
169
+
170
+ GitHub Actions CI template
171
+
172
+ πŸ“œ License
173
+ MIT License
174
+
175
+ πŸ‘¨β€πŸ’» Author
176
+ Lokesh Shimpi
package/bin/cli.js CHANGED
@@ -1,18 +1,25 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
3
  import { runDeploy } from '../src/index.js';
4
+ import { initProject } from '../src/init/initProject.js';
4
5
 
5
6
  const program = new Command();
6
7
 
7
8
  program
8
9
  .name('k8s-deploy')
9
10
  .description('Build and deploy apps to Kubernetes')
10
- .version('0.1.0');
11
+ .version('0.3.0');
12
+
13
+ program
14
+ .command('init')
15
+ .description('Initialize Dockerfile and k8s.config.json')
16
+ .action(initProject);
11
17
 
12
18
  program
13
19
  .command('deploy')
14
- .description('Build Docker image and deploy to Kubernetes')
20
+ .description('Build and deploy app to Kubernetes')
15
21
  .option('-c, --config <path>', 'Config file path', 'k8s.config.json')
22
+ .option('--local', 'Deploy using local Docker image (skip push)')
16
23
  .action(runDeploy);
17
24
 
18
- program.parse();
25
+ program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "k8s-deploy-helper",
3
- "version": "0.1.1",
3
+ "version": "1.0.0",
4
4
  "description": "CLI tool to build, push and deploy applications to Kubernetes",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,18 +1,16 @@
1
1
  import { execa } from 'execa';
2
2
  import fs from 'fs';
3
3
 
4
- export async function buildAndPushImage(config) {
4
+ export async function buildAndPushImage(config, isLocal) {
5
5
  const { image, tag, context, dockerfile } = config.docker;
6
-
6
+ const fullImageName = `${image}:${tag}`;
7
7
  const dockerfilePath = dockerfile || 'Dockerfile';
8
8
 
9
9
  if (!fs.existsSync(dockerfilePath)) {
10
- throw new Error(`Dockerfile not found at path: ${dockerfilePath}`);
10
+ throw new Error(`Dockerfile not found at ${dockerfilePath}`);
11
11
  }
12
12
 
13
- const fullImageName = `${image}:${tag}`;
14
-
15
- console.log(`\n🐳 Building Docker image: ${fullImageName}`);
13
+ console.log(`🐳 Building Docker image: ${fullImageName}`);
16
14
 
17
15
  await execa(
18
16
  'docker',
@@ -20,8 +18,12 @@ export async function buildAndPushImage(config) {
20
18
  { stdio: 'inherit' }
21
19
  );
22
20
 
23
- console.log(`\nπŸ“€ Pushing Docker image: ${fullImageName}`);
21
+ if (isLocal) {
22
+ console.log('πŸ“¦ Local mode enabled β€” skipping image push');
23
+ return fullImageName;
24
+ }
24
25
 
26
+ console.log(`πŸ“€ Pushing Docker image: ${fullImageName}`);
25
27
  await execa('docker', ['push', fullImageName], { stdio: 'inherit' });
26
28
 
27
29
  return fullImageName;
package/src/index.js CHANGED
@@ -1,27 +1,48 @@
1
1
  import { loadConfig } from './config/loadConfig.js';
2
2
  import { validateConfig } from './config/validateConfig.js';
3
+
4
+ import { checkRequiredFiles } from './preflight/checkFiles.js';
5
+ import { checkEnvironment } from './preflight/checkEnvironment.js';
6
+
3
7
  import { buildAndPushImage } from './docker/buildPush.js';
8
+
4
9
  import { generateDeployment } from './k8s/deployment.js';
5
10
  import { generateService } from './k8s/service.js';
6
11
  import { applyKubernetes } from './k8s/apply.js';
7
12
 
8
13
  export async function runDeploy(options) {
9
- console.log('Loading config...');
10
- const config = loadConfig(options.config);
11
- validateConfig(config);
14
+ try {
15
+ console.log('πŸ” Checking required files...');
16
+ checkRequiredFiles();
12
17
 
13
- console.log(`App: ${config.appName}`);
18
+ console.log('πŸ” Checking environment...');
19
+ await checkEnvironment();
14
20
 
15
- let image = `${config.docker.image}:${config.docker.tag}`;
21
+ const configPath = options.config || 'k8s.config.json';
22
+ const isLocal = options.local === true;
16
23
 
17
- try {
18
- image = await buildAndPushImage(config);
19
- } catch {
20
- console.warn('\n⚠️ Image push failed β€” continuing deployment');
21
- }
24
+ console.log('πŸ“„ Loading configuration...');
25
+ const config = loadConfig(configPath);
26
+ validateConfig(config);
27
+
28
+ console.log(`πŸš€ Deploying application: ${config.appName}`);
29
+ if (isLocal) {
30
+ console.log('πŸ“¦ Local mode enabled (image push will be skipped)');
31
+ }
32
+
33
+ const image = await buildAndPushImage(config, isLocal);
22
34
 
23
- generateDeployment(config, image);
24
- generateService(config);
35
+ console.log('πŸ›  Generating Kubernetes manifests...');
36
+ generateDeployment(config, image, isLocal);
37
+ generateService(config);
25
38
 
26
- await applyKubernetes(config);
39
+ console.log('☸️ Applying resources to Kubernetes...');
40
+ await applyKubernetes(config);
41
+
42
+ console.log('\nβœ… Deployment completed successfully');
43
+ } catch (err) {
44
+ console.error('\n❌ Deployment failed');
45
+ console.error(err.message || err);
46
+ process.exit(1);
47
+ }
27
48
  }
@@ -0,0 +1,49 @@
1
+ import fs from 'fs';
2
+
3
+ export function generateDockerfile() {
4
+ if (fs.existsSync('Dockerfile')) return;
5
+
6
+ const dockerfile = `
7
+ FROM node:18-alpine
8
+ WORKDIR /app
9
+ COPY server/package*.json ./
10
+ RUN npm install
11
+ COPY server .
12
+ EXPOSE 5000
13
+ CMD ["npm", "start"]
14
+ `.trim();
15
+
16
+ fs.writeFileSync('Dockerfile', dockerfile);
17
+ console.log('βœ… Dockerfile created');
18
+ }
19
+
20
+ export function generateK8sConfig() {
21
+ if (fs.existsSync('k8s.config.json')) return;
22
+
23
+ const config = {
24
+ appName: 'my-app',
25
+ namespace: 'default',
26
+ docker: {
27
+ image: 'your-docker-username/my-app',
28
+ tag: 'latest',
29
+ context: '.',
30
+ dockerfile: 'Dockerfile'
31
+ },
32
+ kubernetes: {
33
+ replicas: 1,
34
+ containerPort: 5000,
35
+ servicePort: 80,
36
+ serviceType: 'ClusterIP'
37
+ },
38
+ env: {
39
+ NODE_ENV: 'production'
40
+ }
41
+ };
42
+
43
+ fs.writeFileSync(
44
+ 'k8s.config.json',
45
+ JSON.stringify(config, null, 2)
46
+ );
47
+
48
+ console.log('βœ… k8s.config.json created');
49
+ }
@@ -0,0 +1,11 @@
1
+ import { generateDockerfile, generateK8sConfig } from './generateFiles.js';
2
+
3
+ export function initProject() {
4
+ console.log('πŸ”§ Initializing project for Kubernetes deployment...\n');
5
+
6
+ generateDockerfile();
7
+ generateK8sConfig();
8
+
9
+ console.log('\nπŸŽ‰ Initialization complete');
10
+ console.log('πŸ‘‰ Update k8s.config.json with your Docker username');
11
+ }
@@ -1,7 +1,7 @@
1
1
  import yaml from 'js-yaml';
2
2
  import fs from 'fs';
3
3
 
4
- export function generateDeployment(config, image) {
4
+ export function generateDeployment(config, image, isLocal = false) {
5
5
  const deployment = {
6
6
  apiVersion: 'apps/v1',
7
7
  kind: 'Deployment',
@@ -10,7 +10,7 @@ export function generateDeployment(config, image) {
10
10
  namespace: config.namespace || 'default'
11
11
  },
12
12
  spec: {
13
- replicas: config.kubernetes.replicas,
13
+ replicas: config.kubernetes.replicas || 1,
14
14
  selector: {
15
15
  matchLabels: {
16
16
  app: config.appName
@@ -27,15 +27,19 @@ export function generateDeployment(config, image) {
27
27
  {
28
28
  name: config.appName,
29
29
  image,
30
+ imagePullPolicy: isLocal ? 'IfNotPresent' : 'Always',
30
31
  ports: [
31
32
  {
32
33
  containerPort: config.kubernetes.containerPort
33
34
  }
34
35
  ],
35
36
  env: Object.entries(config.env || {}).map(
36
- ([key, value]) => ({ name: key, value })
37
+ ([key, value]) => ({
38
+ name: key,
39
+ value: String(value)
40
+ })
37
41
  ),
38
- resources: config.resources
42
+ resources: config.resources || undefined
39
43
  }
40
44
  ]
41
45
  }
@@ -43,8 +47,8 @@ export function generateDeployment(config, image) {
43
47
  }
44
48
  };
45
49
 
46
- const yamlContent = yaml.dump(deployment);
50
+ const yamlContent = yaml.dump(deployment, { noRefs: true });
47
51
  fs.writeFileSync('deployment.yaml', yamlContent);
48
52
 
49
- return 'deployment.yaml';
53
+ console.log('πŸ“„ deployment.yaml generated');
50
54
  }
@@ -0,0 +1,16 @@
1
+ import fs from 'fs';
2
+
3
+ export function checkRequiredFiles() {
4
+ const missing = [];
5
+
6
+ if (!fs.existsSync('Dockerfile')) missing.push('Dockerfile');
7
+ if (!fs.existsSync('k8s.config.json')) missing.push('k8s.config.json');
8
+
9
+ if (missing.length) {
10
+ console.error('\n❌ Missing required files:');
11
+ missing.forEach(f => console.error(` - ${f}`));
12
+
13
+ console.error('\nπŸ‘‰ Run: k8s-deploy init');
14
+ process.exit(1);
15
+ }
16
+ }