@akash-chowdhury-24/deployhub 2.0.13 → 2.0.15

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.
@@ -1,5 +1,6 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
+ import { resolveContainerPort } from './dockerfile-expose.js';
3
4
 
4
5
  /**
5
6
  * @param {string} name
@@ -65,6 +66,11 @@ export function generateKubernetesManifests({
65
66
  ? ` imagePullSecrets:\n - name: ${imagePullSecret}\n`
66
67
  : '';
67
68
 
69
+ // Service port stays 80 (Ingress-friendly cluster-facing port).
70
+ // containerPort / targetPort must match the container's listening port (EXPOSE).
71
+ const servicePort = 80;
72
+ const targetPort = port;
73
+
68
74
  const deploymentYaml = `apiVersion: apps/v1
69
75
  kind: Deployment
70
76
  metadata:
@@ -96,9 +102,6 @@ ${pullSecretBlock} containers:
96
102
  cpu: "500m"
97
103
  `;
98
104
 
99
- const servicePort = port === 80 ? 80 : 80;
100
- const targetPort = port;
101
-
102
105
  const serviceYaml = `apiVersion: v1
103
106
  kind: Service
104
107
  metadata:
@@ -116,12 +119,29 @@ spec:
116
119
  return { deploymentYaml, serviceYaml };
117
120
  }
118
121
 
122
+ /**
123
+ * Surgically update only containerPort / targetPort in manifest YAML.
124
+ * Does not change Service `port:`, replicas, resources, env, probes, etc.
125
+ *
126
+ * @param {string} yaml
127
+ * @param {number} port
128
+ * @returns {{ content: string, changed: boolean }}
129
+ */
130
+ export function patchKubernetesManifestPorts(yaml, port) {
131
+ const next = String(yaml)
132
+ .replace(/^([ \t]*-?[ \t]*containerPort:[ \t]*)\d+[ \t]*$/gm, `$1${port}`)
133
+ .replace(/^([ \t]*targetPort:[ \t]*)\d+[ \t]*$/gm, `$1${port}`);
134
+
135
+ return { content: next, changed: next !== yaml };
136
+ }
137
+
119
138
  /**
120
139
  * @param {import('../core/config.js').DeployHubConfig} config
121
140
  * @param {Record<string, Record<string, unknown>>} [environments]
141
+ * @param {{ port?: number }} [options]
122
142
  * @returns {{ appName: string, imageName: string, imageTag: string, port: number, namespace: string, imagePullSecret: string }}
123
143
  */
124
- export function resolveKubernetesManifestOptions(config, environments = {}) {
144
+ export function resolveKubernetesManifestOptions(config, environments = {}, options = {}) {
125
145
  const envList = Object.values(environments);
126
146
  const k8sEnv = envList.find((env) => env.type === 'kubernetes') || {};
127
147
 
@@ -142,13 +162,18 @@ export function resolveKubernetesManifestOptions(config, environments = {}) {
142
162
  process.env.KUBE_IMAGE_PULL_SECRET ||
143
163
  '';
144
164
 
145
- let port = 3000;
146
- if (config.projectType === 'both' && config.backend?.port) {
147
- port = config.backend.port;
165
+ /** @type {number} */
166
+ let port;
167
+ if (typeof options.port === 'number' && Number.isFinite(options.port)) {
168
+ port = options.port;
169
+ } else if (config.projectType === 'both' && config.backend?.port) {
170
+ port = Number(config.backend.port);
148
171
  } else if (config.port) {
149
- port = config.port;
172
+ port = Number(config.port);
150
173
  } else if (config.backend?.port) {
151
- port = config.backend.port;
174
+ port = Number(config.backend.port);
175
+ } else {
176
+ port = 3000;
152
177
  }
153
178
 
154
179
  return {
@@ -161,9 +186,77 @@ export function resolveKubernetesManifestOptions(config, environments = {}) {
161
186
  };
162
187
  }
163
188
 
189
+ /**
190
+ * Resolve manifest options with Dockerfile EXPOSE → config → fallback port.
191
+ *
192
+ * @param {string} cwd
193
+ * @param {import('../core/config.js').DeployHubConfig} config
194
+ * @param {Record<string, Record<string, unknown>>} [environments]
195
+ */
196
+ export async function resolveKubernetesManifestOptionsFromCwd(
197
+ cwd,
198
+ config,
199
+ environments = {}
200
+ ) {
201
+ const { port, source } = await resolveContainerPort(cwd, config);
202
+ return {
203
+ ...resolveKubernetesManifestOptions(config, environments, { port }),
204
+ portSource: source,
205
+ };
206
+ }
207
+
208
+ /**
209
+ * Paths DeployHub normally writes for Kubernetes starter manifests.
210
+ * @param {string} cwd
211
+ * @returns {{ deploymentPath: string, servicePath: string }}
212
+ */
213
+ export function getDefaultKubernetesManifestPaths(cwd) {
214
+ const k8sDir = path.join(cwd, 'k8s');
215
+ return {
216
+ deploymentPath: path.join(k8sDir, 'deployment.yaml'),
217
+ servicePath: path.join(k8sDir, 'service.yaml'),
218
+ };
219
+ }
220
+
221
+ /**
222
+ * Patch containerPort/targetPort in existing k8s/deployment.yaml + service.yaml only.
223
+ *
224
+ * @param {string} cwd
225
+ * @param {number} port
226
+ * @returns {Promise<{ patched: string[], skipped: string[], port: number }>}
227
+ */
228
+ export async function syncKubernetesManifestPorts(cwd, port) {
229
+ const { deploymentPath, servicePath } = getDefaultKubernetesManifestPaths(cwd);
230
+ /** @type {string[]} */
231
+ const patched = [];
232
+ /** @type {string[]} */
233
+ const skipped = [];
234
+
235
+ for (const filePath of [deploymentPath, servicePath]) {
236
+ if (!(await fs.pathExists(filePath))) {
237
+ skipped.push(path.relative(cwd, filePath));
238
+ continue;
239
+ }
240
+ const original = await fs.readFile(filePath, 'utf8');
241
+ const { content, changed } = patchKubernetesManifestPorts(original, port);
242
+ if (changed) {
243
+ await fs.writeFile(filePath, content);
244
+ patched.push(path.relative(cwd, filePath));
245
+ } else {
246
+ skipped.push(path.relative(cwd, filePath));
247
+ }
248
+ }
249
+
250
+ return { patched, skipped, port };
251
+ }
252
+
164
253
  export default {
165
254
  sanitizeK8sName,
166
255
  hasKubernetesManifests,
167
256
  generateKubernetesManifests,
257
+ patchKubernetesManifestPorts,
168
258
  resolveKubernetesManifestOptions,
259
+ resolveKubernetesManifestOptionsFromCwd,
260
+ getDefaultKubernetesManifestPaths,
261
+ syncKubernetesManifestPorts,
169
262
  };
@@ -13,8 +13,9 @@ import path from 'path';
13
13
  * @param {import('../../core/config.js').DeployHubConfig} config
14
14
  * @param {string} artifactDir
15
15
  * @param {string} envName
16
+ * @param {{ buildId: string, semver: string, remoteKey: string }} meta
16
17
  */
17
- async function rollbackTarget(config, artifactDir, envName) {
18
+ async function rollbackTarget(config, artifactDir, envName, meta) {
18
19
  const envConfig = config.environments[envName];
19
20
  if (!envConfig) {
20
21
  throw new Error(`Environment "${envName}" not found in config`);
@@ -24,7 +25,7 @@ async function rollbackTarget(config, artifactDir, envName) {
24
25
  log.info(`Rolling back ${envName} (${envConfig.type || 'server'})...`);
25
26
 
26
27
  const provider = getDeploymentProvider(envConfig.type, config, envName);
27
- await provider.rollback(artifactDir);
28
+ await provider.rollback(artifactDir, meta);
28
29
  }
29
30
 
30
31
  /**
@@ -39,7 +40,7 @@ export async function rollbackToVersion(config, versionOrBuildId, cwd = process.
39
40
  throw new Error('No storage providers configured — cannot download artifact for rollback');
40
41
  }
41
42
 
42
- const history = await loadArtifactHistory(providers, config.project);
43
+ const { entries: history } = await loadArtifactHistory(providers, config.project);
43
44
  const resolved = resolveRollbackTarget(history, versionOrBuildId);
44
45
 
45
46
  if (!resolved.ok) {
@@ -62,15 +63,10 @@ export async function rollbackToVersion(config, versionOrBuildId, cwd = process.
62
63
  const zipPath = path.join(artifactDir, 'artifact.zip');
63
64
  await downloadArtifactEntry(providers, config, entry, zipPath);
64
65
 
66
+ // Extract into artifactDir itself so layout matches a normal build artifact:
67
+ // top-level contents (k8s/, dist/, metadata.json, …) alongside artifact.zip.
65
68
  log.info('Extracting artifact for rollback...');
66
- const extractedDir = path.join(artifactDir, '_extracted');
67
- await fs.emptyDir(extractedDir);
68
- await extractArtifact(artifactDir, extractedDir);
69
-
70
- const extractedDeployment = path.join(extractedDir, 'deployment.json');
71
- if (await fs.pathExists(extractedDeployment)) {
72
- await fs.copy(extractedDeployment, path.join(artifactDir, 'deployment.json'));
73
- }
69
+ await extractArtifact(artifactDir, artifactDir);
74
70
 
75
71
  const targets = config.deploy || [];
76
72
  if (targets.length === 0) {
@@ -78,9 +74,15 @@ export async function rollbackToVersion(config, versionOrBuildId, cwd = process.
78
74
  return { artifactDir, entry };
79
75
  }
80
76
 
77
+ const meta = {
78
+ buildId: entry.buildId,
79
+ semver: entry.semver,
80
+ remoteKey: entry.remoteKey,
81
+ };
82
+
81
83
  log.info('Redeploying previous artifact to server targets...');
82
84
  for (const envName of targets) {
83
- await rollbackTarget(config, artifactDir, envName);
85
+ await rollbackTarget(config, artifactDir, envName, meta);
84
86
  }
85
87
 
86
88
  log.success(`Rollback to buildId=${entry.buildId} complete`);
@@ -10,7 +10,7 @@ import {
10
10
  import {
11
11
  generateKubernetesManifests,
12
12
  hasKubernetesManifests,
13
- resolveKubernetesManifestOptions,
13
+ resolveKubernetesManifestOptionsFromCwd,
14
14
  } from './kubernetes-manifests.js';
15
15
 
16
16
  /**
@@ -134,7 +134,11 @@ export async function ensureKubernetesManifests(
134
134
  return { generated: false };
135
135
  }
136
136
 
137
- const manifestOptions = resolveKubernetesManifestOptions(config, environments);
137
+ const manifestOptions = await resolveKubernetesManifestOptionsFromCwd(
138
+ cwd,
139
+ config,
140
+ environments
141
+ );
138
142
  const { deploymentYaml, serviceYaml } = generateKubernetesManifests(manifestOptions);
139
143
 
140
144
  const k8sDir = path.join(cwd, 'k8s');