petadep 1.0.3 → 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 +50 -35
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "petadep",
3
- "version": "1.0.3",
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,18 @@ 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;
47
55
  }
48
56
 
49
57
  const colors = {
@@ -69,47 +77,47 @@ function prompt(rl, question, defaultValue) {
69
77
  });
70
78
  }
71
79
 
72
- function promptHidden(question) {
80
+ function promptHidden(rl, question) {
73
81
  return new Promise((resolve) => {
74
- const input = process.stdin;
75
- const output = process.stdout;
76
- let value = "";
77
-
78
- output.write(`${question}: `);
79
- input.setRawMode(true);
80
- input.resume();
81
-
82
- function onData(chunk) {
83
- const char = chunk.toString("utf8");
84
- if (char === "\r" || char === "\n") {
85
- input.setRawMode(false);
86
- input.pause();
87
- input.removeListener("data", onData);
88
- output.write("\n");
89
- resolve(value.trim());
90
- return;
91
- }
92
- if (char === "\u0003") {
93
- process.exit(1);
94
- }
95
- if (char === "\u007f") {
96
- value = value.slice(0, -1);
97
- return;
98
- }
99
- value += char;
100
- }
101
-
102
- input.on("data", onData);
82
+ rl.stdoutMuted = true;
83
+ rl.question(`${question}: `, (answer) => {
84
+ rl.stdoutMuted = false;
85
+ resolve(answer.trim());
86
+ });
103
87
  });
104
88
  }
105
89
 
106
- async function promptRequired(rl, question) {
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 } = {}) {
107
108
  let value = "";
108
109
  while (!value) {
109
110
  const answer = await prompt(rl, question, "");
110
- value = answer?.trim() || "";
111
+ const normalized = normalize ? normalize(answer || "") : (answer || "").trim();
112
+ value = normalized;
111
113
  if (!value) {
112
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 = "";
113
121
  }
114
122
  }
115
123
  return value;
@@ -122,8 +130,15 @@ export async function initConfigInteractive() {
122
130
  console.log(color("Let's create your webhook config.", colors.cyan));
123
131
  const port = await prompt(rl, "Port", "8787");
124
132
  const hookPath = await prompt(rl, "Webhook path", "/webhook");
125
- const secretInput = await promptHidden("Secret (leave blank to auto-generate)");
126
- const repo = await promptRequired(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
+ });
127
142
  const branch = await prompt(rl, "Branch", "main");
128
143
  const env = await prompt(rl, "Env name", "production");
129
144
  const workdir = await promptRequired(rl, "Workdir");