auto-deploy-sh 1.0.0 → 2.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/LICENSE CHANGED
@@ -1,15 +1,8 @@
1
- ISC License
1
+ MIT License
2
2
 
3
3
  Copyright (c) 2025 yotomum_ml
4
4
 
5
- Permission to use, copy, modify, and/or distribute this software for any
6
- purpose with or without fee is hereby granted, provided that the above
7
- copyright notice and this permission notice appear in all copies.
5
+ Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software isfurnished to do so, subject to the following conditions:
6
+ The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.
8
7
 
9
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
- PERFORMANCE OF THIS SOFTWARE.
8
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE SOFTWARE.
package/README.md CHANGED
@@ -1,58 +1,121 @@
1
- # Auto-deploy-sh
2
-
3
- English|[简体中文](README_zh_CN.md)
4
-
5
- Cloud service deployment is a crucial aspect of project engineering development and an important way to reach more users with applications. Currently, there are various methods for running projects on the cloud, among which **containerized deployment** has become the mainstream method for delivering cloud-native applications. The technology stack represented by **Docker + Kubernetes** can effectively address core issues such as **environment consistency, resource utilization, and portability** in the software development and deployment process ([Docker Official Documentation](https://docs.docker.com/)).
6
-
7
- In enterprise-level development, project deployment often relies on tools such as GitLab to establish a comprehensive **CI/CD** (Continuous Integration/Continuous Delivery) workflow. However, for individual developers or small teams, setting up and maintaining a complete CI/CD system is not only complex but also costly, making it difficult to implement quickly.
8
-
9
- So, how can individuals or small teams efficiently deploy projects? A common practice nowadays is to directly integrate Docker for deployment through an IDE (such as IntelliJ IDEA). However, this approach typically relies on **paid IDE versions**, requires cumbersome local configuration, and demands the installation of Docker CLI in the development environment, imposing certain technical thresholds and hardware and software requirements on users.
10
-
11
- This project aims to **reduce the deployment threshold**, achieve an automated Docker deployment process with **minimal configuration and minimal dependencies**, and help developers quickly deploy applications to cloud servers without requiring complex environments.
12
-
13
- ## Explanation of configuration file attributes
14
-
15
- The configuration file name is fixed as `deploy-config.json` and stored in the root directory. Since it contains private information, if added manually, please add it to `.gitignore`.
16
- If the file is absent, the `CLI` will guide the addition process and update the content of `.gitignore` under the added location, requiring no additional operations.
17
-
18
- ```javaScript
19
- {
20
- input: 'Remote server IP or domain name',
21
- port: 'SSH port',
22
- user: 'Username',
23
- password: 'password',
24
- beforLaunch:[ // Preparation operations before project release
25
- 'npm run build'
26
- ],
27
- dockerBuildFiles: [
28
- // The files involved in the construction, relative to the path where the script runs (usually the root directory of the project)
29
- 'Dockerfile',
30
- 'dist'
31
- ],
32
- imageTag: '镜像标识', // Format: [Warehouse Address/] [Username/Project Name]: [Label]
33
- containerName: '容器名', // Unique identifier of Docker running instance
34
- BindPorts: '端口映射' // Format: `<Host Port>:<Container Port>`
35
- }
36
- ```
37
-
38
- ## Package release
39
-
40
- ### Partial configuration of the project
41
-
42
- Download: `npm install auto-deploy-sh -D `
43
- Run:
44
-
45
- ```json
46
- // package.json configuration
47
- "scripts": {
48
- "deploy": "auto-deploy-sh"
49
- }
50
- // Run
51
- npm run deploy
52
- ```
53
-
54
- ### 全局配置
55
-
56
- Download: `npm install auto-deploy-sh -g`
57
- Run in the root directory of the project that needs to be deployed: `auto-deploy-sh`
58
-
1
+ # Auto-deploy-sh
2
+
3
+ English|[简体中文](README_zh_CN.md)
4
+
5
+ This project aims to lower deployment barriers by providing an ultra-simple configuration and minimal dependency automated Docker deployment workflow. It helps developers quickly deploy applications to cloud servers without complex environment setups.
6
+
7
+ ## Deployment Commands
8
+
9
+ ### Project-Specific Configuration
10
+
11
+ **Install:**
12
+
13
+ ```bash
14
+ npm install auto-deploy-sh -D
15
+ ```
16
+
17
+ **Run:**
18
+
19
+ - Add to `package.json`:
20
+
21
+ ```json
22
+ "scripts": {
23
+ "deploy": "auto-deploy-sh"
24
+ }
25
+ ```
26
+
27
+ 2. Execute in terminal:
28
+
29
+ ```bash
30
+ npm run deploy
31
+ ```
32
+
33
+ ### Global Configuration
34
+
35
+ **Install:**
36
+
37
+ ```bash
38
+ npm install auto-deploy-sh -g
39
+ ```
40
+
41
+ **Run** (in the root directory of the project to deploy):
42
+
43
+ ```bash
44
+ auto-deploy-sh
45
+ ```
46
+
47
+ ### Advanced Usage
48
+
49
+ Run with a **custom configuration file**:
50
+
51
+ ```bash
52
+ auto-deploy <config-path>
53
+ # OR
54
+ auto-deploy -f <config-path>
55
+ ```
56
+
57
+ Useful for multi-environment deployment (e.g., development, staging, production).
58
+
59
+ ## Configuration File Reference
60
+
61
+ The configuration file is **fixed as `deploy-config.json`**. If missing, the tool will **guide you to create it** (stored in the command execution directory by default) and automatically add `deploy-config.json` to `.gitignore` to **prevent accidental commits of sensitive data**.
62
+
63
+ > ⚠️ **Critical Note**: The configuration file contains sensitive information (e.g., passwords). If creating manually, ensure `deploy-config.json` is added to `.gitignore`.
64
+
65
+ ### Configuration Properties
66
+
67
+ - `host`: Remote server IP address or domain name.
68
+ - `port`: SSH port number.
69
+ - `user`: SSH username for the remote server.
70
+ - `password`: Password for the SSH user.
71
+ - `beforLaunch`: **Pre-deployment commands** (array of strings) to execute locally before starting the deployment (e.g., `["npm run build"]`).
72
+ - `Dockerfile`: **Optional**. If omitted: 1. Check `dockerBuildFiles` for a file named `Dockerfile`; 2. If not found, search for `Dockerfile` in the **first-level directory of the current execution environment**.
73
+ - `dockerBuildFiles`: Files/directories to include in the Docker build context (required for Dockerfile execution).
74
+ - `imageTag`: Docker image tag, formatted as `[registry-url/][username/project-name]:[tag]` (e.g., `my-registry.com/user/my-app:v1.0`).
75
+ - `containerName`: Unique name for the running container (ensures no conflicts with existing containers).
76
+ - `BindPorts`: Port mapping, formatted as `<host-port>:<container-port>` (e.g., `"8080:80"`).
77
+ - `Options`: **Optional** advanced settings (all sub-properties are optional)
78
+ - `volumes`: Volume mappings (array of strings), formatted as `[host-path/volume-name]:[container-path]:[optional-flags]` (e.g., `["/host/data:/container/data:ro"]`).
79
+ - `networks`: Connect the container to a custom Docker network (created via `docker network create <network-name>`) for inter-container communication.
80
+
81
+ ### Format Introduction
82
+
83
+ ```json
84
+ {
85
+ "host": "string",
86
+ "port": "number | string",
87
+ "user": "string",
88
+ "password": "string",
89
+ "beforLaunch": "string[]",
90
+ "Dockerfile": "string",
91
+ "dockerBuildFiles": "string[]",
92
+ "imageTag": "string",
93
+ "containerName": "string",
94
+ "BindPorts": "string",
95
+ "Options": {
96
+ "volumes": "string[]",
97
+ "networks": "string"
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### Full Configuration Example
103
+
104
+ ```json
105
+ {
106
+ "host": "your-server-ip",
107
+ "port": 22,
108
+ "user": "ssh-username",
109
+ "password": "ssh-password",
110
+ "beforLaunch": ["npm run build"],
111
+ "Dockerfile": "./Dockerfile",
112
+ "dockerBuildFiles": ["./dist", "./package.json"],
113
+ "imageTag": "my-app:latest",
114
+ "containerName": "my-app-container",
115
+ "BindPorts": "80:80",
116
+ "Options": {
117
+ "volumes": ["/host/logs:/app/logs:rw"],
118
+ "networks": "my-custom-network"
119
+ }
120
+ }
121
+ ```
@@ -3,28 +3,32 @@ import path from 'path';
3
3
  const configMethod = {
4
4
  host: {
5
5
  type: 'input',
6
- message: '远程服务器 IP 或域名: ',
6
+ message: 'Remote server IP or domain name: ',
7
7
  },
8
8
  port: {
9
9
  type: 'number',
10
- message: 'SSH 端口: ',
10
+ message: 'SSH port: ',
11
11
  },
12
12
  user: {
13
13
  type: 'input',
14
- message: '用户名: ',
14
+ message: 'username: ',
15
15
  },
16
16
  password: {
17
17
  type: 'input',
18
- message: '密码: ',
18
+ message: 'password: ',
19
19
  },
20
20
  beforLaunch: {
21
21
  type: 'input',
22
- message: '项目发布前的前置命令(befor launch) 多个以 , 隔开: ',
22
+ message: 'Before launch (multiple commands separated by , ): ',
23
23
  handleFn: (value) => value.split(','),
24
24
  },
25
+ Dockerfile: {
26
+ type: 'input',
27
+ message: 'Select the Dockerfile you need to execute.',
28
+ },
25
29
  dockerBuildFiles: {
26
30
  type: 'input',
27
- message: 'docker构建所需的文件相对根目录的路径(多个 , 隔开): ',
31
+ message: 'Path to the file relative to the root directory required for Docker build (separated by , ): ',
28
32
  handleFn: (value) => value.split(','),
29
33
  },
30
34
  imageTag: {
@@ -40,6 +44,17 @@ const configMethod = {
40
44
  message: 'Bind Ports(such as 8080:8080): ',
41
45
  },
42
46
  };
47
+ const optionsMethod = {
48
+ volumes: {
49
+ type: 'input',
50
+ message: 'Multiple volumes are divided into by , : ',
51
+ handleFn: (value) => value.split(','),
52
+ },
53
+ networks: {
54
+ type: 'input',
55
+ message: 'Bound network: ',
56
+ },
57
+ };
43
58
  export async function createConfig(rootPath) {
44
59
  const config = {};
45
60
  const keys = Object.keys(configMethod);
@@ -56,6 +71,28 @@ export async function createConfig(rootPath) {
56
71
  const value = (await inquirer.invoke(configMethod[key]));
57
72
  config[key] = fn(value);
58
73
  }
74
+ const options = await inquirer.invoke({
75
+ type: 'checkbox',
76
+ message: 'Select other options as needed.',
77
+ choices: [
78
+ { name: 'Volumes', value: 'volumes' },
79
+ { name: 'Networks', value: 'networks' },
80
+ ],
81
+ });
82
+ config['Options'] = {};
83
+ for (let i = 0; i < options.length; ++i) {
84
+ const key = options[i];
85
+ const fn = Object.hasOwn(optionsMethod[key], 'handleFn')
86
+ ? optionsMethod[key].handleFn
87
+ : (value) => {
88
+ if (typeof value === 'string')
89
+ return value.trim();
90
+ else
91
+ return value;
92
+ };
93
+ const value = (await inquirer.invoke(optionsMethod[key]));
94
+ config['Options'][key] = fn(value);
95
+ }
59
96
  await fs.writeFileSync(path.resolve(rootPath, 'deploy-config.json'), JSON.stringify(config, null, 2));
60
97
  const gitIgnorePath = path.resolve(rootPath, '.gitignore');
61
98
  let contain = '';
@@ -27,5 +27,7 @@ export declare class Deploy {
27
27
  errMsg?: string;
28
28
  }, command: string): Promise<void>;
29
29
  dockcerImageBuild(ssh: NodeSSH): Promise<void>;
30
+ buildDocker(ssh: NodeSSH, IMAGE_TAG: string, REMOTEAPPPATH: string, TARGET_DIR: string, CONTAINER_NAME: string): Promise<void>;
31
+ runDocker(ssh: NodeSSH, CONTAINER_NAME: string): Promise<void>;
30
32
  clear(ssh: NodeSSH): Promise<void>;
31
33
  }
@@ -59,6 +59,10 @@ export class Deploy {
59
59
  const files = [];
60
60
  if (this.config.dockerBuildFiles && this.config.dockerBuildFiles.length > 0) {
61
61
  let dockerfileFlag = false, dockerignoreFlag = false;
62
+ if (this.config.Dockerfile && this.config.Dockerfile.length > 0) {
63
+ dockerfileFlag = true;
64
+ this.config.dockerBuildFiles.push(this.config.Dockerfile);
65
+ }
62
66
  for (let i = 0; i < this.config.dockerBuildFiles.length; ++i) {
63
67
  const file = this.config.dockerBuildFiles[i];
64
68
  if (/\bDockerfile\b/.test(file)) {
@@ -76,6 +80,7 @@ export class Deploy {
76
80
  if (!dockerignoreFlag && fs._.existsSync(path.resolve(this.context, '.dockerignore'))) {
77
81
  this.config.dockerBuildFiles.push('.dockerignore');
78
82
  }
83
+ let isEmptyFile = false;
79
84
  this.config.dockerBuildFiles.forEach(async (file) => {
80
85
  const fullPath = path.resolve(this.context, file);
81
86
  if (fs._.existsSync(fullPath)) {
@@ -83,9 +88,11 @@ export class Deploy {
83
88
  }
84
89
  else {
85
90
  log.error(`The file name is ${file}, and the file does not exist`);
86
- throw new Error();
91
+ isEmptyFile = true;
87
92
  }
88
93
  });
94
+ if (isEmptyFile)
95
+ throw new Error();
89
96
  }
90
97
  else {
91
98
  log.error(`The file to build the image cannot be empty. Please check whether dockerBuildFiles is set correctly`);
@@ -112,6 +119,10 @@ export class Deploy {
112
119
  archive.directory(file, fileName);
113
120
  }
114
121
  else {
122
+ if (this.config.Dockerfile && fileName === path.basename(this.config.Dockerfile)) {
123
+ archive.file(file, { name: 'Dockerfile' });
124
+ continue;
125
+ }
115
126
  archive.file(file, { name: fileName });
116
127
  }
117
128
  }
@@ -124,11 +135,11 @@ export class Deploy {
124
135
  if (await fs.exist(uploadPath)) {
125
136
  const state = await fs.getFileStat(uploadPath);
126
137
  const localSize = state.size;
127
- await ora.start('📤 开始上传...', async () => {
138
+ await ora.start('📤 Start uploading...', async () => {
128
139
  await ssh.putFile(uploadPath, this.remotePath);
129
140
  }, status => {
130
141
  if (status)
131
- return `文件上传完成`;
142
+ return `File upload complete.`;
132
143
  else {
133
144
  exit(1);
134
145
  return chalk.error(`exit code: 1`);
@@ -136,17 +147,17 @@ export class Deploy {
136
147
  });
137
148
  const exists = await ssh.execCommand(`test -f "${this.remotePath}" && echo "yes" || echo "no"`);
138
149
  if (exists.stdout.trim() !== 'yes') {
139
- log.error('文件上传检测失败');
150
+ log.error('File upload failed.');
140
151
  throw new Error();
141
152
  }
142
153
  const sizeResult = await ssh.execCommand(`stat -c %s "${this.remotePath}"`);
143
154
  const remoteSize = parseInt(sizeResult.stdout.trim(), 10);
144
155
  if (isNaN(remoteSize)) {
145
- log.error('无法获取远程文件大小');
156
+ log.error('Unable to obtain remote file size');
146
157
  throw new Error();
147
158
  }
148
159
  if (remoteSize !== localSize && remoteSize < localSize - 10) {
149
- log.error(`文件大小不匹配: 本地=${localSize}, 远程=${remoteSize}`);
160
+ log.error(`File size mismatch: local=${localSize}, remote=${remoteSize}`);
150
161
  throw new Error();
151
162
  }
152
163
  }
@@ -186,43 +197,73 @@ export class Deploy {
186
197
  mkdir -p "${TARGET_DIR}"
187
198
 
188
199
  if [ ! -f "${REMOTE_PATH}" ]; then
189
- echo "❌ 错误:压缩包不存在: ${REMOTE_PATH}";
200
+ echo "❌ Error: Compressed file does not exist.: ${REMOTE_PATH}";
190
201
  exit 1
191
202
  fi
192
203
  tar -xzf "${REMOTE_PATH}" -C "${TARGET_DIR}";
193
204
  `);
194
- await this.execCommand(ssh, {
195
- startMsg: `🐳 构建镜像: ${IMAGE_TAG}...`,
196
- succMsg: `🐳 构建镜像: ${IMAGE_TAG}成功.`,
197
- }, `
205
+ await this.buildDocker(ssh, IMAGE_TAG, REMOTEAPPPATH, TARGET_DIR, CONTAINER_NAME);
206
+ await this.runDocker(ssh, CONTAINER_NAME);
207
+ log.done('✔ Deployment completed.');
208
+ }
209
+ catch {
210
+ log.error('✖ Deployment failed.');
211
+ }
212
+ finally {
213
+ await this.clear(ssh);
214
+ }
215
+ }
216
+ async buildDocker(ssh, IMAGE_TAG, REMOTEAPPPATH, TARGET_DIR, CONTAINER_NAME) {
217
+ await this.execCommand(ssh, {
218
+ startMsg: `🐳 Build image: ${IMAGE_TAG}...`,
219
+ succMsg: `🐳 Build image: ${IMAGE_TAG} success.`,
220
+ }, `
198
221
  cd "${REMOTEAPPPATH}"
199
222
  docker build -t ${IMAGE_TAG} ${TARGET_DIR}
200
223
  `);
201
- await this.execCommand(ssh, {
202
- startMsg: `🔄 停止并清理旧容器: ${CONTAINER_NAME}...`,
203
- succMsg: `旧容器已经清理成功.`,
204
- }, `
224
+ await this.execCommand(ssh, {
225
+ startMsg: `🔄 Stop and clean the old container.: ${CONTAINER_NAME}...`,
226
+ succMsg: `The old container has been successfully cleaned.`,
227
+ }, `
205
228
  if docker inspect "${CONTAINER_NAME}" >/dev/null 2>&1; then
206
229
  docker stop "${CONTAINER_NAME}";
207
230
  docker rm "${CONTAINER_NAME}";
208
231
  else
209
- echo "ℹ️ 容器 ${CONTAINER_NAME} 不存在,跳过清理";
232
+ echo "ℹ️ container ${CONTAINER_NAME}. Does not exist, skip cleaning.";
210
233
  fi
211
234
  `);
212
- await this.execCommand(ssh, {
213
- startMsg: `🐳 启动容器: ${CONTAINER_NAME}...`,
214
- succMsg: `🐳 启动容器成功.`,
215
- }, `
216
- docker run -d --name ${CONTAINER_NAME} -p ${this.config.BindPorts} ${IMAGE_TAG}
217
- `);
218
- log.done('✔ 部署完成');
219
- }
220
- catch {
221
- log.error('✖ 部署失败');
222
- }
223
- finally {
224
- await this.clear(ssh);
235
+ }
236
+ async runDocker(ssh, CONTAINER_NAME) {
237
+ let optionsCLI = `--name ${CONTAINER_NAME} -p ${this.config.BindPorts}`;
238
+ const options = this.config.Options;
239
+ if (options) {
240
+ const optionsKeys = Object.keys(options);
241
+ if (optionsKeys.length > 0) {
242
+ for (let i = 0; i < optionsKeys.length; ++i) {
243
+ const key = optionsKeys[i];
244
+ switch (key) {
245
+ case 'volumes': {
246
+ let volumes = options.volumes.map(volume => `-v ${volume}`).join(' ');
247
+ optionsCLI += ' ' + volumes;
248
+ break;
249
+ }
250
+ case 'networks': {
251
+ optionsCLI += ` --network ${options.networks}`;
252
+ break;
253
+ }
254
+ default:
255
+ break;
256
+ }
257
+ }
258
+ }
225
259
  }
260
+ optionsCLI += ` ${this.config.imageTag}`;
261
+ await this.execCommand(ssh, {
262
+ startMsg: `🐳 Start container: ${CONTAINER_NAME}...`,
263
+ succMsg: `🐳 Container started successfully.`,
264
+ }, `
265
+ docker run -d ${optionsCLI}
266
+ `);
226
267
  }
227
268
  async clear(ssh) {
228
269
  await ssh.execCommand(`rm -f ${this.remotePath}`);
@@ -1 +1 @@
1
- export {};
1
+ export declare function deploySSH(configFile?: string): Promise<void>;
package/dist/bin/index.js CHANGED
@@ -1,13 +1,12 @@
1
- #!/usr/bin/env node
2
1
  import path from 'path';
3
2
  import { NodeSSH } from 'node-ssh';
4
3
  import { log, ora, exit, chalk, fs } from "./utils/index.js";
5
4
  import { Deploy } from "./deploy.js";
6
5
  import { createConfig } from "./config.js";
7
- const ssh = new NodeSSH();
8
- const CONFIG_FILE = 'deploy-config.json';
9
- const CONFIG_FILE_PATH = path.resolve(process.cwd(), `./${CONFIG_FILE}`);
10
- async function deploySSH() {
6
+ export async function deploySSH(configFile = 'deploy-config.json') {
7
+ const ssh = new NodeSSH();
8
+ const CONFIG_FILE = configFile ?? 'deploy-config.json';
9
+ const CONFIG_FILE_PATH = path.resolve(process.cwd(), CONFIG_FILE);
11
10
  if (await fs.exist(CONFIG_FILE_PATH)) {
12
11
  let config = JSON.parse((await fs.readDirOrFile(CONFIG_FILE_PATH, { encoding: 'utf-8' })));
13
12
  if (!config.BindPorts.includes(':')) {
@@ -21,9 +20,9 @@ async function deploySSH() {
21
20
  password: config.password,
22
21
  }), status => {
23
22
  if (status)
24
- return `SSH连接成功!`;
23
+ return `SSH connection successful!`;
25
24
  else {
26
- log.error('SSH连接失败!');
25
+ log.error('SSH connection failed!');
27
26
  exit(1);
28
27
  return chalk.error(`exit code: 1`);
29
28
  }
@@ -34,17 +33,16 @@ async function deploySSH() {
34
33
  await deploy.dockcerImageBuild(ssh);
35
34
  }
36
35
  catch {
37
- exit(1);
38
36
  }
39
37
  finally {
40
38
  fs.removeSync(path.resolve(process.cwd(), deploy.uploadFileName));
41
39
  ssh.dispose();
40
+ exit(1);
42
41
  }
43
42
  }
44
43
  else {
45
- log.error(`⚠️ 在根目录下未找到配置文件 ${CONFIG_FILE},将引导创建... `);
44
+ log.error(`⚠️ No configuration file found in the root directory ${CONFIG_FILE}. Will guide the creation... `);
46
45
  await createConfig(process.cwd());
47
46
  deploySSH();
48
47
  }
49
48
  }
50
- deploySSH();
@@ -5,10 +5,15 @@ export type sshConfig = {
5
5
  user: string;
6
6
  password: string;
7
7
  beforLaunch: string[];
8
+ Dockerfile: string;
8
9
  dockerBuildFiles: string[];
9
10
  imageTag: string;
10
11
  containerName: string;
11
12
  BindPorts: string;
13
+ Options: {
14
+ volumes: string[];
15
+ networks: string;
16
+ };
12
17
  };
13
18
  export type fileData = string | NodeJS.ArrayBufferView;
14
19
  import type { promises } from 'fs';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import { fs } from "../bin/utils/fse.js";
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { deploySSH } from "../bin/index.js";
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ let pkgPath = path.join(__dirname, '../package.json');
10
+ if (!(await fs.exist(pkgPath))) {
11
+ pkgPath = path.join(__dirname, '../../package.json');
12
+ }
13
+ const packageJson = JSON.parse((await fs.readDirOrFile(pkgPath, { encoding: 'utf-8' })));
14
+ program
15
+ .name(packageJson.name)
16
+ .description(packageJson.description)
17
+ .version(`v${packageJson.version}`)
18
+ .argument('[path]', 'deployment configuration file')
19
+ .option('-f, --file <path>', 'set deployment configuration file', 'deploy-config.json')
20
+ .action((path, options) => {
21
+ const targetPath = path ?? options.file;
22
+ if (typeof targetPath !== 'string' || targetPath.trim() === '') {
23
+ throw new Error(`Invalid path: ${targetPath}, path must be a non-empty string`);
24
+ }
25
+ deploySSH(targetPath);
26
+ })
27
+ .parse();
package/package.json CHANGED
@@ -1,81 +1,82 @@
1
- {
2
- "name": "auto-deploy-sh",
3
- "version": "1.0.0",
4
- "description": "Automated Docker deployment tool",
5
- "bin": {
6
- "auto-deploy-sh": "./dist/bin/index.js"
7
- },
8
- "files": [
9
- "dist",
10
- "package.json",
11
- "README.md"
12
- ],
13
- "type": "module",
14
- "main": "./dist/bin/index.js",
15
- "scripts": {
16
- "build": "npm run clean && tsc -p tsconfig.bulid.json",
17
- "clean": "rimraf dist build",
18
- "deploy": "node ./ts-node bin/index.ts",
19
- "lint:eslint": "eslint --cache --ext .ts,.vue,.js,.jsx,.tsx,.json",
20
- "lint:eslint:fix": "eslint --cache --ext .ts,.vue,.js,.jsx,.tsx,.json --fix",
21
- "prepare": "husky",
22
- "prepack": "npm run build",
23
- "postbuild": "node ./postbuild.js"
24
- },
25
- "keywords": [
26
- "Automated",
27
- "Lower deployment threshold",
28
- "deployment",
29
- "Minimalist configuration"
30
- ],
31
- "repository": {
32
- "type": "git",
33
- "url": "https://github.com/yotomum-ml/auto-deploy-sh.git"
34
- },
35
- "author": "yotomum_ml",
36
- "license": "ISC",
37
- "lint-staged": {
38
- "*.{js,ts,jsx,tsx,vue,css,json}": [
39
- "eslint"
40
- ],
41
- "*.md": [
42
- "prettier --check"
43
- ]
44
- },
45
- "dependencies": {
46
- "archiver": "^7.0.1",
47
- "chalk": "^5.6.2",
48
- "execa": "^9.6.0",
49
- "fs-extra": "^11.3.1",
50
- "inquirer": "^12.9.6",
51
- "node-ssh": "^13.2.1",
52
- "ora": "^8.2.0",
53
- "readline-sync": "^1.4.10",
54
- "ssh2": "^1.17.0",
55
- "ts-node": "^10.9.2"
56
- },
57
- "engines": {
58
- "node": ">=18.0.0"
59
- },
60
- "devDependencies": {
61
- "@commitlint/cli": "^19.8.1",
62
- "@commitlint/config-conventional": "^19.8.1",
63
- "@eslint/css": "^0.8.1",
64
- "@eslint/js": "^9.29.0",
65
- "@eslint/json": "^0.12.0",
66
- "@stylistic/eslint-plugin": "^4.4.1",
67
- "@types/archiver": "^6.0.3",
68
- "@types/fs-extra": "^11.0.4",
69
- "auto-deploy-sh": "file:auto-deploy-sh-1.0.0.tgz",
70
- "eslint": "^9.27.0",
71
- "eslint-config-prettier": "^10.1.5",
72
- "eslint-plugin-prettier": "^5.4.1",
73
- "eslint-plugin-vue": "^10.2.0",
74
- "husky": "^9.1.7",
75
- "lint-staged": "^15.5.2",
76
- "node-notifier": "^10.0.1",
77
- "rimraf": "^5.0.10",
78
- "typescript": "^5.8.3",
79
- "typescript-eslint": "^8.34.1"
80
- }
81
- }
1
+ {
2
+ "name": "auto-deploy-sh",
3
+ "version": "2.0.0",
4
+ "description": "Automated Docker deployment tool",
5
+ "bin": {
6
+ "auto-deploy-sh": "./dist/cli/index.js"
7
+ },
8
+ "files": [
9
+ "dist",
10
+ "package.json",
11
+ "README.md"
12
+ ],
13
+ "type": "module",
14
+ "main": "./dist/cli/index.js",
15
+ "scripts": {
16
+ "build": "npm run clean && tsc -p tsconfig.bulid.json",
17
+ "clean": "rimraf dist build",
18
+ "deploy": "node --loader ts-node/esm cli/index.ts",
19
+ "lint:doc": "prettier **/*.md --check",
20
+ "lint:doc:fix": "prettier **/*.md --write",
21
+ "lint:eslint": "eslint --cache --ext .ts,.vue,.js,.jsx,.tsx,.json",
22
+ "lint:eslint:fix": "eslint --cache --ext .ts,.vue,.js,.jsx,.tsx,.json --fix",
23
+ "prepare": "husky",
24
+ "prepack": "npm run build",
25
+ "postbuild": "node ./postbuild.js"
26
+ },
27
+ "keywords": [
28
+ "Automated",
29
+ "Lower deployment threshold",
30
+ "deployment",
31
+ "Minimalist configuration"
32
+ ],
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/yotomum-ml/auto-deploy-sh.git"
36
+ },
37
+ "author": "yotomum_ml",
38
+ "license": "ISC",
39
+ "lint-staged": {
40
+ "*.{js,ts,jsx,tsx,vue,css,json}": [
41
+ "eslint --no-warn-ignored"
42
+ ],
43
+ "*.md": [
44
+ "prettier --check --no-warn-ignored"
45
+ ]
46
+ },
47
+ "dependencies": {
48
+ "archiver": "^7.0.1",
49
+ "chalk": "^5.6.2",
50
+ "commander": "^13.1.0",
51
+ "execa": "^9.6.0",
52
+ "fs-extra": "^11.3.1",
53
+ "inquirer": "^12.9.6",
54
+ "node-ssh": "^13.2.1",
55
+ "ora": "^8.2.0",
56
+ "ssh2": "^1.17.0",
57
+ "ts-node": "^10.9.2"
58
+ },
59
+ "engines": {
60
+ "node": ">=18.0.0"
61
+ },
62
+ "devDependencies": {
63
+ "@commitlint/cli": "^19.8.1",
64
+ "@commitlint/config-conventional": "^19.8.1",
65
+ "@eslint/css": "^0.8.1",
66
+ "@eslint/js": "^9.29.0",
67
+ "@eslint/json": "^0.12.0",
68
+ "@stylistic/eslint-plugin": "^4.4.1",
69
+ "@types/archiver": "^6.0.3",
70
+ "@types/fs-extra": "^11.0.4",
71
+ "eslint": "^9.27.0",
72
+ "eslint-config-prettier": "^10.1.5",
73
+ "eslint-plugin-prettier": "^5.4.1",
74
+ "eslint-plugin-vue": "^10.2.0",
75
+ "husky": "^9.1.7",
76
+ "lint-staged": "^15.5.2",
77
+ "node-notifier": "^10.0.1",
78
+ "rimraf": "^5.0.10",
79
+ "typescript": "^5.8.3",
80
+ "typescript-eslint": "^8.34.1"
81
+ }
82
+ }