@akash-chowdhury-24/deployhub 2.0.31 → 2.0.32

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.
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Docker remote-host mode: local daemon, first-class SSH (node-ssh), or raw
3
+ * DOCKER_HOST (Docker CLI ssh:// / tcp:// transport).
4
+ *
5
+ * Kubernetes must not import or honor this module — a Kubernetes deploy talks
6
+ * to the cluster via kubectl, not a remote Docker daemon.
7
+ */
8
+
9
+ import { shellQuote } from './shell-quote.js';
10
+ import { createSshExecSession } from '../deployment/ssh-connection.js';
11
+ import { resolveDockerRemoteMode } from './docker-remote-mode.js';
12
+
13
+ export { resolveDockerRemoteMode };
14
+
15
+ /**
16
+ * SSH identity for docker remote.mode === "ssh".
17
+ * Same env names as ec2 (SSH_HOST / SSH_USER / SSH_KEY_PATH / SSH_KEY).
18
+ * Per-environment prefixing already separates these from a sibling ssh/ec2 env.
19
+ *
20
+ * @param {Record<string, unknown>} settings
21
+ * @param {Record<string, string|undefined>} env
22
+ */
23
+ export function resolveDockerSshTarget(settings, env = process.env) {
24
+ const host = String(settings.host || env.SSH_HOST || '');
25
+ const user = String(settings.user || env.SSH_USER || '');
26
+ const keyPath = settings.keyPath || env.SSH_KEY_PATH;
27
+ const sshKey = env.SSH_KEY;
28
+ const sshPort = Number(env.SSH_SSH_PORT || settings.sshPort) || 22;
29
+ return { host, user, keyPath, sshKey, sshPort };
30
+ }
31
+
32
+ /**
33
+ * @param {string} host
34
+ * @param {string} user
35
+ * @returns {string}
36
+ */
37
+ export function formatRemoteDockerSshFailure(host, user) {
38
+ return (
39
+ `Could not reach ${host} via SSH as '${user}'. Check host,\n` +
40
+ `username, and key path.`
41
+ );
42
+ }
43
+
44
+ /**
45
+ * @param {string} host
46
+ * @param {string} user
47
+ * @returns {string}
48
+ */
49
+ export function formatRemoteDockerNotInstalled(host, user) {
50
+ return (
51
+ `Docker is not installed on the remote host (${user}@${host}).\n` +
52
+ `Install Docker on the server first: https://docs.docker.com/engine/install/`
53
+ );
54
+ }
55
+
56
+ /**
57
+ * @param {string} host
58
+ * @param {string} user
59
+ * @returns {string}
60
+ */
61
+ export function formatRemoteDockerPermissionDenied(host, user) {
62
+ return (
63
+ `SSH user '${user}' cannot access the Docker daemon on ${host}\n` +
64
+ `(permission denied).\n` +
65
+ `Run this on the remote server, then reconnect your SSH session:\n` +
66
+ `sudo usermod -aG docker ${user}`
67
+ );
68
+ }
69
+
70
+ /**
71
+ * @param {string} host
72
+ * @param {string} user
73
+ * @returns {string}
74
+ */
75
+ export function formatRemoteDockerDaemonOk(host, user) {
76
+ return `Remote Docker daemon reachable (${user}@${host})`;
77
+ }
78
+
79
+ /**
80
+ * @param {{ code?: number|null, stdout?: string, stderr?: string }} result
81
+ * @returns {'ok'|'permission'|'not-installed'|'other'}
82
+ */
83
+ export function classifyRemoteDockerPs(result) {
84
+ const stdout = String(result?.stdout || '');
85
+ const stderr = String(result?.stderr || '');
86
+ const combined = `${stderr}\n${stdout}`.toLowerCase();
87
+ const code = result?.code;
88
+
89
+ if (code === 0 || code === null) {
90
+ return 'ok';
91
+ }
92
+
93
+ if (
94
+ combined.includes('permission denied') ||
95
+ combined.includes('got permission denied while trying to connect to the docker daemon')
96
+ ) {
97
+ return 'permission';
98
+ }
99
+
100
+ if (
101
+ code === 127 ||
102
+ combined.includes('command not found') ||
103
+ /docker:\s*not found/.test(combined) ||
104
+ (combined.includes('no such file or directory') && combined.includes('docker'))
105
+ ) {
106
+ return 'not-installed';
107
+ }
108
+
109
+ return 'other';
110
+ }
111
+
112
+ /**
113
+ * Probe `docker ps` over the shared node-ssh session. Never throws — doctor
114
+ * wraps each check independently.
115
+ *
116
+ * @param {{
117
+ * host: string,
118
+ * user: string,
119
+ * keyPath?: string,
120
+ * sshKey?: string,
121
+ * sshPort?: number,
122
+ * env?: Record<string, string|undefined>,
123
+ * }} target
124
+ * @returns {Promise<{
125
+ * sshOk: boolean,
126
+ * sshError?: string,
127
+ * kind?: ReturnType<typeof classifyRemoteDockerPs>,
128
+ * detail?: string,
129
+ * host: string,
130
+ * user: string,
131
+ * }>}
132
+ */
133
+ export async function probeRemoteDockerPs(target) {
134
+ const host = target.host;
135
+ const user = target.user;
136
+ if (!host || !user) {
137
+ return {
138
+ sshOk: false,
139
+ sshError: formatRemoteDockerSshFailure(host || '(missing host)', user || '(missing user)'),
140
+ host: host || '',
141
+ user: user || '',
142
+ };
143
+ }
144
+
145
+ const session = createSshExecSession({
146
+ host,
147
+ user,
148
+ keyPath: target.keyPath ? String(target.keyPath) : undefined,
149
+ sshKey: target.sshKey,
150
+ sshPort: target.sshPort,
151
+ env: target.env,
152
+ });
153
+
154
+ /** @type {import('node-ssh').NodeSSH | undefined} */
155
+ let ssh;
156
+ try {
157
+ ssh = await session.connect();
158
+ const result = await session.execUnchecked(ssh, 'docker ps');
159
+ const kind = classifyRemoteDockerPs(result);
160
+ const detail = String(result.stderr || result.stdout || '').trim();
161
+ return { sshOk: true, kind, detail, host, user };
162
+ } catch (err) {
163
+ const msg = err instanceof Error ? err.message : String(err);
164
+ return {
165
+ sshOk: false,
166
+ sshError: formatRemoteDockerSshFailure(host, user),
167
+ detail: msg,
168
+ host,
169
+ user,
170
+ };
171
+ } finally {
172
+ if (ssh) ssh.dispose();
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Quoted remote docker commands (image/container/env interpolated via shellQuote).
178
+ * @param {string} imageRef
179
+ * @param {string} containerName
180
+ * @param {Record<string, string>} [runEnv]
181
+ */
182
+ export function buildRemoteDockerCommands(imageRef, containerName, runEnv = {}) {
183
+ const image = shellQuote(imageRef);
184
+ const name = shellQuote(containerName);
185
+ /** @type {string[]} */
186
+ const envFlags = [];
187
+ for (const [key, value] of Object.entries(runEnv)) {
188
+ envFlags.push(`-e ${shellQuote(`${key}=${value}`)}`);
189
+ }
190
+ const envArg = envFlags.length > 0 ? `${envFlags.join(' ')} ` : '';
191
+
192
+ return {
193
+ stop: `docker stop ${name} 2>/dev/null || true`,
194
+ rm: `docker rm -f ${name} 2>/dev/null || true`,
195
+ pull: `docker pull ${image}`,
196
+ run: `docker run -d --rm --name ${name} ${envArg}${image}`,
197
+ ps: `docker ps --filter ${shellQuote(`name=^/${containerName}$`)} --format ${shellQuote('{{.Status}}')}`,
198
+ info: 'docker info',
199
+ /**
200
+ * @param {string} registry
201
+ * @param {string} username
202
+ * @param {string} token
203
+ */
204
+ login: (registry, username, token) =>
205
+ `echo ${shellQuote(token)} | docker login ${shellQuote(registry)} -u ${shellQuote(username)} --password-stdin`,
206
+ };
207
+ }
208
+
209
+ export default {
210
+ resolveDockerRemoteMode,
211
+ resolveDockerSshTarget,
212
+ probeRemoteDockerPs,
213
+ buildRemoteDockerCommands,
214
+ };
@@ -21,6 +21,7 @@ import {
21
21
  getEnvTrigger,
22
22
  getEnabledEnvironmentNames,
23
23
  isEnvEnabled,
24
+ getEnvSettings,
24
25
  } from '../core/environments.js';
25
26
  import { resolvePhpVersion } from './php-version.js';
26
27
 
@@ -540,7 +541,11 @@ export function buildWorkflowEnvEntries(
540
541
 
541
542
  const method = getEnvMethod(env);
542
543
  if (!method) continue;
543
- const unprefixedKeys = getDeploymentWorkflowSecretKeys(method, config);
544
+ const unprefixedKeys = getDeploymentWorkflowSecretKeys(
545
+ method,
546
+ config,
547
+ getEnvSettings(env)
548
+ );
544
549
  for (const key of unprefixedKeys) {
545
550
  const secretName = envUsesPrefixedSecrets(envName, cfg)
546
551
  ? prefixSecretKey(envName, key)