petadep 1.0.2 → 1.0.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +80 -42
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "petadep",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Deploy GitHub repos to a VPS on push via webhooks",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -40,10 +40,31 @@ export async function initConfig({ configPath }) {
40
40
  }
41
41
 
42
42
  function createInterface() {
43
- return readline.createInterface({
43
+ const rl = readline.createInterface({
44
44
  input: process.stdin,
45
45
  output: process.stdout,
46
+ terminal: true,
46
47
  });
48
+ rl._writeToOutput = function writeToOutput(stringToWrite) {
49
+ if (this.stdoutMuted) {
50
+ return;
51
+ }
52
+ this.output.write(stringToWrite);
53
+ };
54
+ return rl;
55
+ }
56
+
57
+ const colors = {
58
+ reset: "\u001b[0m",
59
+ bold: "\u001b[1m",
60
+ cyan: "\u001b[36m",
61
+ green: "\u001b[32m",
62
+ yellow: "\u001b[33m",
63
+ magenta: "\u001b[35m",
64
+ };
65
+
66
+ function color(text, code) {
67
+ return `${code}${text}${colors.reset}`;
47
68
  }
48
69
 
49
70
  function prompt(rl, question, defaultValue) {
@@ -56,50 +77,71 @@ function prompt(rl, question, defaultValue) {
56
77
  });
57
78
  }
58
79
 
59
- function promptHidden(question) {
80
+ function promptHidden(rl, question) {
60
81
  return new Promise((resolve) => {
61
- const input = process.stdin;
62
- const output = process.stdout;
63
- let value = "";
64
-
65
- output.write(`${question}: `);
66
- input.setRawMode(true);
67
- input.resume();
68
-
69
- function onData(chunk) {
70
- const char = chunk.toString("utf8");
71
- if (char === "\r" || char === "\n") {
72
- input.setRawMode(false);
73
- input.pause();
74
- input.removeListener("data", onData);
75
- output.write("\n");
76
- resolve(value.trim());
77
- return;
78
- }
79
- if (char === "\u0003") {
80
- process.exit(1);
81
- }
82
- if (char === "\u007f") {
83
- value = value.slice(0, -1);
84
- return;
85
- }
86
- value += char;
87
- }
88
-
89
- input.on("data", onData);
82
+ rl.stdoutMuted = true;
83
+ rl.question(`${question}: `, (answer) => {
84
+ rl.stdoutMuted = false;
85
+ resolve(answer.trim());
86
+ });
90
87
  });
91
88
  }
92
89
 
90
+ function normalizeRepo(input) {
91
+ const trimmed = input.trim();
92
+ const sshMatch = trimmed.match(/^git@github\.com:([^/]+\/[^/]+?)(\.git)?$/);
93
+ if (sshMatch) {
94
+ return sshMatch[1];
95
+ }
96
+ const httpsMatch = trimmed.match(/^https?:\/\/github\.com\/([^/]+\/[^/]+?)(\.git)?$/);
97
+ if (httpsMatch) {
98
+ return httpsMatch[1];
99
+ }
100
+ return trimmed;
101
+ }
102
+
103
+ function isValidRepoFormat(value) {
104
+ return /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(value);
105
+ }
106
+
107
+ async function promptRequired(rl, question, { normalize, validate, hint } = {}) {
108
+ let value = "";
109
+ while (!value) {
110
+ const answer = await prompt(rl, question, "");
111
+ const normalized = normalize ? normalize(answer || "") : (answer || "").trim();
112
+ value = normalized;
113
+ if (!value) {
114
+ console.log(color("This value is required. Try again.", colors.yellow));
115
+ continue;
116
+ }
117
+ if (validate && !validate(value)) {
118
+ const message = hint || "Invalid value. Try again.";
119
+ console.log(color(message, colors.yellow));
120
+ value = "";
121
+ }
122
+ }
123
+ return value;
124
+ }
125
+
93
126
  export async function initConfigInteractive() {
94
127
  const rl = createInterface();
95
128
  try {
129
+ console.log(color("petadep setup", colors.bold + colors.magenta));
130
+ console.log(color("Let's create your webhook config.", colors.cyan));
96
131
  const port = await prompt(rl, "Port", "8787");
97
132
  const hookPath = await prompt(rl, "Webhook path", "/webhook");
98
- const secretInput = await promptHidden("Secret (leave blank to auto-generate)");
99
- const repo = await prompt(rl, "Repo (owner/repo)", "");
133
+ const secretInput = await promptHidden(
134
+ rl,
135
+ "Secret (leave blank to auto-generate)"
136
+ );
137
+ const repo = await promptRequired(rl, "Repo (owner/repo)", {
138
+ normalize: normalizeRepo,
139
+ validate: isValidRepoFormat,
140
+ hint: "Use owner/repo (example: toonami2907/ecommerce-pole). Git URLs are also accepted.",
141
+ });
100
142
  const branch = await prompt(rl, "Branch", "main");
101
143
  const env = await prompt(rl, "Env name", "production");
102
- const workdir = await prompt(rl, "Workdir", "");
144
+ const workdir = await promptRequired(rl, "Workdir");
103
145
  const script = await prompt(rl, "Script path", "./deploy.sh");
104
146
  const logsDir = await prompt(rl, "Logs dir", "./logs");
105
147
  const sshKeyPath = await prompt(rl, "SSH key path (optional)", "");
@@ -120,19 +162,15 @@ export async function initConfigInteractive() {
120
162
  delete config.sshKeyPath;
121
163
  }
122
164
 
123
- if (repo && workdir) {
124
- config.deployments = [
125
- { repo, branch, env, workdir, script },
126
- ];
127
- }
165
+ config.deployments = [{ repo, branch, env, workdir, script }];
128
166
 
129
167
  const resolvedPath = path.resolve(savePath);
130
168
  await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
131
169
  await fs.writeFile(resolvedPath, JSON.stringify(config, null, 2));
132
170
 
133
- console.log("Config created:", resolvedPath);
134
- console.log("Webhook secret:", secret);
135
- console.log(`Start agent: petadep agent --config ${resolvedPath}`);
171
+ console.log(color("Config created:", colors.green), resolvedPath);
172
+ console.log(color("Webhook secret:", colors.green), secret);
173
+ console.log(color("Start agent:", colors.cyan), `petadep agent --config ${resolvedPath}`);
136
174
  } finally {
137
175
  rl.close();
138
176
  }