@tertium/hlpr 0.5.5 → 0.5.6
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
|
@@ -45,24 +45,13 @@ hlpr -f ssh init dir
|
|
|
45
45
|
|
|
46
46
|
## Available Commands
|
|
47
47
|
|
|
48
|
-
### Shell Script Commands
|
|
49
|
-
|
|
50
|
-
Shell scripts support variable substitution using `{{variable}}` syntax.
|
|
51
|
-
|
|
52
|
-
- `git fodd` - Git command
|
|
53
|
-
- `hello world` - Hello world example
|
|
54
|
-
- `ssh init dir` - SSH initialization
|
|
55
|
-
|
|
56
48
|
### TypeScript Commands
|
|
57
49
|
|
|
58
|
-
TypeScript commands are implemented as `.ts` files and run directly with Bun.
|
|
59
|
-
|
|
60
50
|
#### file rename
|
|
61
51
|
|
|
62
52
|
Recursively rename files and folders according to a specific case style.
|
|
63
53
|
|
|
64
54
|
**Supported styles:**
|
|
65
|
-
|
|
66
55
|
- `title_underscore` - Title_Case_With_Underscores
|
|
67
56
|
- `snake` - snake_case_lowercase
|
|
68
57
|
- `kebab` - kebab-case-lowercase
|
|
@@ -71,8 +60,7 @@ Recursively rename files and folders according to a specific case style.
|
|
|
71
60
|
- `upper` - UPPER_CASE_WITH_UNDERSCORES
|
|
72
61
|
- `lower` - lower_case_with_underscores
|
|
73
62
|
|
|
74
|
-
**
|
|
75
|
-
|
|
63
|
+
**Usage:**
|
|
76
64
|
```bash
|
|
77
65
|
# Dry run (preview changes without applying)
|
|
78
66
|
hlpr file rename ./my-project kebab --dry
|
|
@@ -84,15 +72,99 @@ hlpr file rename ./docs pascal
|
|
|
84
72
|
```
|
|
85
73
|
|
|
86
74
|
**Features:**
|
|
87
|
-
|
|
88
75
|
- ✅ Recursive directory traversal
|
|
89
|
-
- ✅ Extension preservation
|
|
76
|
+
- ✅ Extension preservation
|
|
90
77
|
- ✅ Leading dot file support (e.g., `.gitignore`)
|
|
91
78
|
- ✅ Case-only rename handling (Windows compatibility)
|
|
92
79
|
- ✅ Conflict resolution (adds `_N` suffix)
|
|
93
80
|
- ✅ Dry-run mode (`--dry` or `-n`)
|
|
94
81
|
|
|
95
|
-
See `commands/file/rename/README.md` for
|
|
82
|
+
**See:** [`src/commands/file/rename/README.md`](src/commands/file/rename/README.md) for full documentation.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
#### help
|
|
87
|
+
|
|
88
|
+
Display help information about hlpr commands.
|
|
89
|
+
|
|
90
|
+
**Usage:**
|
|
91
|
+
```bash
|
|
92
|
+
hlpr help
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### Shell Script Commands
|
|
98
|
+
|
|
99
|
+
Shell scripts support variable substitution using `{{variable}}` syntax.
|
|
100
|
+
|
|
101
|
+
#### git
|
|
102
|
+
|
|
103
|
+
Helpful git utilities for common development workflows.
|
|
104
|
+
|
|
105
|
+
**Usage:**
|
|
106
|
+
```bash
|
|
107
|
+
hlpr git <command>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Commands:**
|
|
111
|
+
- `fodd` - Fetch and update develop branch from origin
|
|
112
|
+
- `precommit` - Run build and stage binaries before commit
|
|
113
|
+
- `switch-clean` - Safely switch to a branch and delete the previous one (local & remote)
|
|
114
|
+
|
|
115
|
+
**See:** [`src/commands/git/README.md`](src/commands/git/README.md) for full documentation.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
#### hello
|
|
120
|
+
|
|
121
|
+
Example/demo commands for testing the hlpr CLI tool.
|
|
122
|
+
|
|
123
|
+
**Usage:**
|
|
124
|
+
```bash
|
|
125
|
+
hlpr hello world
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Prompts for input and prints a personalized greeting with variable substitution.
|
|
129
|
+
|
|
130
|
+
**See:** [`src/commands/hello/README.md`](src/commands/hello/README.md) for full documentation.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
#### nvm
|
|
135
|
+
|
|
136
|
+
Node Version Manager utilities for managing Node.js versions.
|
|
137
|
+
|
|
138
|
+
**Usage:**
|
|
139
|
+
```bash
|
|
140
|
+
hlpr nvm <command>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Commands:**
|
|
144
|
+
- `install` - Install Node Version Manager (nvm)
|
|
145
|
+
- `lts` - Install and use Node.js LTS version
|
|
146
|
+
|
|
147
|
+
**See:** [`src/commands/nvm/README.md`](src/commands/nvm/README.md) for full documentation.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
#### ssh
|
|
152
|
+
|
|
153
|
+
SSH configuration and setup utilities.
|
|
154
|
+
|
|
155
|
+
**Usage:**
|
|
156
|
+
```bash
|
|
157
|
+
hlpr ssh init-dir
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Initializes SSH directory with proper permissions:
|
|
161
|
+
- `~/.ssh/` directory with 700 permissions
|
|
162
|
+
- `~/.ssh/known_hosts` with 644 permissions
|
|
163
|
+
- `~/.ssh/config` with 644 permissions
|
|
164
|
+
|
|
165
|
+
Portable across Unix/Linux/macOS and Git Bash on Windows.
|
|
166
|
+
|
|
167
|
+
**See:** [`src/commands/ssh/README.md`](src/commands/ssh/README.md) for full documentation.
|
|
96
168
|
|
|
97
169
|
## How It Works
|
|
98
170
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tertium/hlpr",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "Windows and *Nix utility for typical programming activity",
|
|
5
5
|
"author": "Vitalii Balabanov",
|
|
6
6
|
"email": "tertiumnon@gmail.com",
|
|
@@ -8,18 +8,20 @@
|
|
|
8
8
|
"type": "module",
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build:main": "bun build ./src/index.ts --outfile ./bin/index.js --target node",
|
|
11
|
-
"build:commands": "
|
|
11
|
+
"build:commands": "bun build-commands.js",
|
|
12
12
|
"build": "bun run build:main && bun run build:commands",
|
|
13
13
|
"test": "bun test",
|
|
14
|
-
"test:e2e": "
|
|
14
|
+
"test:e2e": "bun scripts/test-rename-e2e.cjs",
|
|
15
15
|
"prepublishOnly": "bun run build",
|
|
16
|
-
"release:minor": "
|
|
17
|
-
"release:patch": "
|
|
18
|
-
"release:major": "
|
|
16
|
+
"release:minor": "bun node_modules/@tertium/js/scripts/release.js minor",
|
|
17
|
+
"release:patch": "bun node_modules/@tertium/js/scripts/release.js patch",
|
|
18
|
+
"release:major": "bun node_modules/@tertium/js/scripts/release.js major",
|
|
19
19
|
"prepare": "husky"
|
|
20
20
|
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@tertium/js": "^1.4.6"
|
|
23
|
+
},
|
|
21
24
|
"devDependencies": {
|
|
22
|
-
"@tertium/js": "^0.16.0",
|
|
23
25
|
"@types/node": "^22.13.10",
|
|
24
26
|
"husky": "^9.1.7",
|
|
25
27
|
"typescript": "^5.8.3"
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# @description Deploy application to remote server with environment variables
|
|
3
|
-
|
|
4
|
-
SKIP_BUILD="{{SKIP_BUILD}}"
|
|
5
|
-
DEPLOY_USER="{{DEPLOY_USER}}"
|
|
6
|
-
DEPLOY_HOST="{{DEPLOY_HOST}}"
|
|
7
|
-
DEPLOY_PATH="{{DEPLOY_PATH}}"
|
|
8
|
-
APP_NAME="{{APP_NAME}}"
|
|
9
|
-
|
|
10
|
-
echo "Deploying to $DEPLOY_HOST:$DEPLOY_PATH"
|
|
11
|
-
echo "=========================================="
|
|
12
|
-
|
|
13
|
-
# Build phase (unless skip-build is set)
|
|
14
|
-
if [ "$SKIP_BUILD" != "true" ]; then
|
|
15
|
-
echo "Building..."
|
|
16
|
-
bun run build
|
|
17
|
-
if [ $? -ne 0 ]; then
|
|
18
|
-
echo "✗ Build failed"
|
|
19
|
-
exit 1
|
|
20
|
-
fi
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
# Deploy phase
|
|
24
|
-
echo ""
|
|
25
|
-
echo "Copying files to remote server..."
|
|
26
|
-
scp -r dist/ $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/
|
|
27
|
-
if [ $? -ne 0 ]; then
|
|
28
|
-
echo "✗ SCP failed"
|
|
29
|
-
exit 1
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
echo ""
|
|
33
|
-
echo "Installing dependencies and restarting service..."
|
|
34
|
-
ssh $DEPLOY_USER@$DEPLOY_HOST "cd $DEPLOY_PATH && bun install --production && pm2 restart $APP_NAME --update-env"
|
|
35
|
-
if [ $? -ne 0 ]; then
|
|
36
|
-
echo "✗ Remote deployment failed"
|
|
37
|
-
exit 1
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
echo ""
|
|
41
|
-
echo "✓ Deployment complete!"
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/commands/deploy/deploy.ts
|
|
4
|
-
import { execSync } from "child_process";
|
|
5
|
-
import { existsSync, readFileSync } from "fs";
|
|
6
|
-
import * as path from "path";
|
|
7
|
-
function loadEnv(projectDir) {
|
|
8
|
-
const envPath = path.join(projectDir, ".env");
|
|
9
|
-
if (!existsSync(envPath)) {
|
|
10
|
-
console.error("Error: .env file not found");
|
|
11
|
-
console.error(`Please copy .env.example to .env in ${projectDir}`);
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
const env = {};
|
|
15
|
-
const content = readFileSync(envPath, "utf-8");
|
|
16
|
-
content.split(`
|
|
17
|
-
`).forEach((line) => {
|
|
18
|
-
const trimmed = line.trim();
|
|
19
|
-
if (!trimmed || trimmed.startsWith("#"))
|
|
20
|
-
return;
|
|
21
|
-
const [key, ...valueParts] = trimmed.split("=");
|
|
22
|
-
if (key && valueParts.length > 0) {
|
|
23
|
-
env[key.trim()] = valueParts.join("=").trim();
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
return env;
|
|
27
|
-
}
|
|
28
|
-
function validate(env) {
|
|
29
|
-
const required = ["DEPLOY_USER", "DEPLOY_HOST", "DEPLOY_PATH", "APP_NAME"];
|
|
30
|
-
const missing = required.filter((key) => !env[key]);
|
|
31
|
-
if (missing.length > 0) {
|
|
32
|
-
console.error(`Error: Missing environment variables: ${missing.join(", ")}`);
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function deploy() {
|
|
37
|
-
const projectDir = process.cwd();
|
|
38
|
-
const env = loadEnv(projectDir);
|
|
39
|
-
validate(env);
|
|
40
|
-
console.log(`
|
|
41
|
-
Deploying to ${env.DEPLOY_HOST}:${env.DEPLOY_PATH}
|
|
42
|
-
`);
|
|
43
|
-
try {
|
|
44
|
-
const scpCmd = `scp -r dist/ ${env.DEPLOY_USER}@${env.DEPLOY_HOST}:${env.DEPLOY_PATH}/`;
|
|
45
|
-
console.log(`→ ${scpCmd}`);
|
|
46
|
-
execSync(scpCmd, {
|
|
47
|
-
stdio: "inherit",
|
|
48
|
-
cwd: projectDir,
|
|
49
|
-
shell: true
|
|
50
|
-
});
|
|
51
|
-
const sshCmd = `ssh ${env.DEPLOY_USER}@${env.DEPLOY_HOST} "zsh -i -c 'cd ${env.DEPLOY_PATH} && bun install --production && (pm2 restart ${env.APP_NAME} --update-env || pm2 start dist/index.js --name ${env.APP_NAME} --update-env)'"`;
|
|
52
|
-
console.log(`→ ${sshCmd}`);
|
|
53
|
-
execSync(sshCmd, {
|
|
54
|
-
stdio: "inherit",
|
|
55
|
-
cwd: projectDir,
|
|
56
|
-
shell: true
|
|
57
|
-
});
|
|
58
|
-
console.log(`
|
|
59
|
-
✓ Deployment complete!
|
|
60
|
-
`);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.error(`
|
|
63
|
-
✗ Deployment failed: ${error.message}
|
|
64
|
-
`);
|
|
65
|
-
process.exit(1);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
deploy();
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
// src/commands/deploy/deploy.service.ts
|
|
2
|
-
import { execSync } from "node:child_process";
|
|
3
|
-
import * as fs from "node:fs";
|
|
4
|
-
|
|
5
|
-
class DeployService {
|
|
6
|
-
config;
|
|
7
|
-
constructor(config) {
|
|
8
|
-
this.config = config;
|
|
9
|
-
}
|
|
10
|
-
log(message) {
|
|
11
|
-
console.log(`[${new Date().toISOString()}] ${message}`);
|
|
12
|
-
}
|
|
13
|
-
async copyToServer() {
|
|
14
|
-
this.log("Copying dist folder to remote server...");
|
|
15
|
-
const { remoteHost, remoteUser, deployPath, localDist, envFile } = this.config;
|
|
16
|
-
if (!fs.existsSync(localDist)) {
|
|
17
|
-
throw new Error(`Local dist folder not found: ${localDist}`);
|
|
18
|
-
}
|
|
19
|
-
const sshHost = `${remoteUser}@${remoteHost}`;
|
|
20
|
-
const tempPath = `${deployPath}_tmp`;
|
|
21
|
-
try {
|
|
22
|
-
this.log(`Creating temp directory on remote: ${tempPath}`);
|
|
23
|
-
execSync(`ssh ${sshHost} "mkdir -p ${tempPath} && rm -rf ${tempPath}/*"`, { stdio: "inherit" });
|
|
24
|
-
this.log(`Copying ${localDist} to ${sshHost}:${tempPath}/`);
|
|
25
|
-
execSync(`scp -r "${localDist}" ${sshHost}:${tempPath}/dist`, {
|
|
26
|
-
stdio: "inherit"
|
|
27
|
-
});
|
|
28
|
-
if (envFile && fs.existsSync(envFile)) {
|
|
29
|
-
this.log(`Copying ${envFile} to remote`);
|
|
30
|
-
execSync(`scp "${envFile}" ${sshHost}:${tempPath}/.env`, {
|
|
31
|
-
stdio: "inherit"
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
if (fs.existsSync("package.json")) {
|
|
35
|
-
this.log("Copying package.json to remote");
|
|
36
|
-
execSync(`scp package.json ${sshHost}:${tempPath}/`, {
|
|
37
|
-
stdio: "inherit"
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
this.log(`Moving files from temp to deploy path...`);
|
|
41
|
-
const moveCmd = `rm -rf ${deployPath}/dist && ` + `mv ${tempPath}/dist ${deployPath}/ && ` + `[ -f ${tempPath}/.env ] && mv ${tempPath}/.env ${deployPath}/.env || true && ` + `[ -f ${tempPath}/package.json ] && mv ${tempPath}/package.json ${deployPath}/ || true && ` + `rm -rf ${tempPath}`;
|
|
42
|
-
execSync(`ssh ${sshHost} "${moveCmd}"`, { stdio: "inherit" });
|
|
43
|
-
this.log("✓ Files copied successfully");
|
|
44
|
-
} catch (error) {
|
|
45
|
-
throw new Error(`Failed to copy files: ${error instanceof Error ? error.message : String(error)}`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
async installDependencies() {
|
|
49
|
-
if (this.config.skipInstall) {
|
|
50
|
-
this.log("Skipping dependency installation");
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
this.log("Installing dependencies on server...");
|
|
54
|
-
const { remoteHost, remoteUser, deployPath } = this.config;
|
|
55
|
-
const sshHost = `${remoteUser}@${remoteHost}`;
|
|
56
|
-
try {
|
|
57
|
-
const cmd = `cd ${deployPath} && bun install --production`;
|
|
58
|
-
execSync(`ssh ${sshHost} "${cmd}"`, { stdio: "inherit" });
|
|
59
|
-
this.log("✓ Dependencies installed");
|
|
60
|
-
} catch (error) {
|
|
61
|
-
this.log("⚠ Dependency installation skipped or failed");
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async restartProcess() {
|
|
65
|
-
if (this.config.skipRestart) {
|
|
66
|
-
this.log("Skipping process restart");
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
this.log("Restarting application via PM2...");
|
|
70
|
-
const { remoteHost, remoteUser, deployPath, appName, port } = this.config;
|
|
71
|
-
const sshHost = `${remoteUser}@${remoteHost}`;
|
|
72
|
-
try {
|
|
73
|
-
const portOpt = port ? ` --env PORT=${port}` : "";
|
|
74
|
-
const cmd = `cd ${deployPath} && ` + `(pm2 restart ${appName} --update-env 2>/dev/null || ` + `pm2 start dist/index.js --name ${appName}${portOpt} 2>/dev/null) && ` + `pm2 save 2>/dev/null || true`;
|
|
75
|
-
execSync(`ssh ${sshHost} "bash -l -c '${cmd}'"`, { stdio: "inherit" });
|
|
76
|
-
this.log("✓ Process restart attempted");
|
|
77
|
-
} catch (error) {
|
|
78
|
-
this.log("⚠ PM2 restart skipped - may need manual setup");
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
async deploy() {
|
|
82
|
-
const startTime = new Date().toISOString();
|
|
83
|
-
try {
|
|
84
|
-
this.log("Starting deployment...");
|
|
85
|
-
await this.copyToServer();
|
|
86
|
-
await this.installDependencies();
|
|
87
|
-
await this.restartProcess();
|
|
88
|
-
this.log("✅ Deployment completed successfully");
|
|
89
|
-
return {
|
|
90
|
-
success: true,
|
|
91
|
-
message: "Deployment completed successfully",
|
|
92
|
-
timestamp: startTime
|
|
93
|
-
};
|
|
94
|
-
} catch (error) {
|
|
95
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
96
|
-
this.log(`❌ Deployment failed: ${message}`);
|
|
97
|
-
return {
|
|
98
|
-
success: false,
|
|
99
|
-
message,
|
|
100
|
-
timestamp: startTime
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
async function createDeployService(config) {
|
|
106
|
-
return new DeployService(config);
|
|
107
|
-
}
|
|
108
|
-
export {
|
|
109
|
-
createDeployService,
|
|
110
|
-
DeployService
|
|
111
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# @description Deploy application to remote server
|
|
3
|
-
|
|
4
|
-
echo "Building..."
|
|
5
|
-
bun run build
|
|
6
|
-
echo ""
|
|
7
|
-
echo "Copying files to remote server..."
|
|
8
|
-
scp -r dist/ {{DEPLOY_USER}}@{{DEPLOY_HOST}}:{{DEPLOY_PATH}}/
|
|
9
|
-
echo ""
|
|
10
|
-
echo "Installing dependencies and restarting service..."
|
|
11
|
-
ssh {{DEPLOY_USER}}@{{DEPLOY_HOST}} "cd {{DEPLOY_PATH}} && bun install --production && pm2 restart {{APP_NAME}} --update-env"
|
|
12
|
-
echo ""
|
|
13
|
-
echo "✓ Deployment complete!"
|
|
File without changes
|