@uns-kit/cli 0.0.12 → 0.0.13

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/dist/index.js +119 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,14 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import { execFile } from "node:child_process";
2
3
  import { access, cp, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
3
4
  import path from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import { createRequire } from "node:module";
6
7
  import process from "node:process";
7
8
  import readline from "node:readline/promises";
9
+ import { promisify } from "node:util";
8
10
  const __filename = fileURLToPath(import.meta.url);
9
11
  const __dirname = path.dirname(__filename);
10
12
  const require = createRequire(import.meta.url);
11
13
  const coreVersion = resolveCoreVersion();
14
+ const execFileAsync = promisify(execFile);
12
15
  async function main() {
13
16
  const args = process.argv.slice(2);
14
17
  const command = args[0];
@@ -91,7 +94,21 @@ async function configureDevops(targetPath) {
91
94
  }
92
95
  const pkg = JSON.parse(pkgRaw);
93
96
  const config = JSON.parse(configRaw);
94
- const defaultOrg = config.devops?.organization ?? "sijit";
97
+ await ensureGitRepository(targetDir);
98
+ const remoteUrl = await getGitRemoteUrl(targetDir, "origin");
99
+ let gitRemoteMessage;
100
+ let ensuredRemoteUrl = remoteUrl;
101
+ if (!ensuredRemoteUrl) {
102
+ const remoteAnswer = (await promptQuestion("Azure DevOps repository URL (e.g. https://dev.azure.com/sijit/industry40/_git/my-repo): ")).trim();
103
+ if (!remoteAnswer) {
104
+ throw new Error("A repository URL is required to set the git remote origin.");
105
+ }
106
+ await addGitRemote(targetDir, "origin", remoteAnswer);
107
+ ensuredRemoteUrl = remoteAnswer;
108
+ gitRemoteMessage = ` Added git remote origin -> ${remoteAnswer}`;
109
+ }
110
+ const inferredOrganization = ensuredRemoteUrl ? inferAzureOrganization(ensuredRemoteUrl) : undefined;
111
+ const defaultOrg = config.devops?.organization?.trim() || inferredOrganization || "sijit";
95
112
  const answer = (await promptQuestion(`Azure DevOps organization [${defaultOrg}]: `)).trim();
96
113
  const organization = answer || defaultOrg;
97
114
  if (!config.devops || typeof config.devops !== "object") {
@@ -124,6 +141,9 @@ async function configureDevops(targetPath) {
124
141
  }
125
142
  console.log(`\nDevOps tooling configured.`);
126
143
  console.log(` Azure organization: ${organization}`);
144
+ if (gitRemoteMessage) {
145
+ console.log(gitRemoteMessage);
146
+ }
127
147
  if (pkgChanged) {
128
148
  console.log(" Updated package.json scripts/devDependencies. Run pnpm install to fetch new packages.");
129
149
  }
@@ -131,6 +151,104 @@ async function configureDevops(targetPath) {
131
151
  console.log(" Existing package.json already contained required entries.");
132
152
  }
133
153
  }
154
+ async function ensureGitRepository(dir) {
155
+ try {
156
+ const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
157
+ cwd: dir,
158
+ encoding: "utf8",
159
+ });
160
+ if (stdout.trim() !== "true") {
161
+ throw new Error(`Directory ${dir} is not a git repository.`);
162
+ }
163
+ }
164
+ catch (error) {
165
+ if (isGitCommandNotFoundError(error)) {
166
+ throw new Error("Git is required to run configure-devops but was not found in PATH.");
167
+ }
168
+ const execError = error;
169
+ const stderr = typeof execError.stderr === "string" ? execError.stderr : "";
170
+ if (stderr.includes("not a git repository")) {
171
+ throw new Error(`Directory ${dir} is not a git repository.`);
172
+ }
173
+ throw error;
174
+ }
175
+ }
176
+ async function getGitRemoteUrl(dir, remoteName) {
177
+ try {
178
+ const { stdout } = await execFileAsync("git", ["remote", "get-url", remoteName], {
179
+ cwd: dir,
180
+ encoding: "utf8",
181
+ });
182
+ return stdout.trim();
183
+ }
184
+ catch (error) {
185
+ if (isGitCommandNotFoundError(error)) {
186
+ throw new Error("Git is required to run configure-devops but was not found in PATH.");
187
+ }
188
+ if (isGitRemoteMissingError(error)) {
189
+ return null;
190
+ }
191
+ throw error;
192
+ }
193
+ }
194
+ async function addGitRemote(dir, remoteName, remoteUrl) {
195
+ try {
196
+ await execFileAsync("git", ["remote", "add", remoteName, remoteUrl], {
197
+ cwd: dir,
198
+ encoding: "utf8",
199
+ });
200
+ }
201
+ catch (error) {
202
+ if (isGitCommandNotFoundError(error)) {
203
+ throw new Error("Git is required to run configure-devops but was not found in PATH.");
204
+ }
205
+ const execError = error;
206
+ const stderr = typeof execError.stderr === "string" ? execError.stderr.trim() : "";
207
+ if (stderr) {
208
+ throw new Error(`Failed to add git remote origin: ${stderr}`);
209
+ }
210
+ throw new Error(`Failed to add git remote origin: ${error.message}`);
211
+ }
212
+ }
213
+ function inferAzureOrganization(remoteUrl) {
214
+ try {
215
+ const url = new URL(remoteUrl);
216
+ const hostname = url.hostname.toLowerCase();
217
+ if (hostname === "dev.azure.com" || hostname.endsWith(".visualstudio.com")) {
218
+ const segments = url.pathname.split("/").filter(Boolean);
219
+ if (segments.length >= 1) {
220
+ return segments[0];
221
+ }
222
+ }
223
+ }
224
+ catch (error) {
225
+ // ignore parse errors for non-HTTP(S) remotes
226
+ }
227
+ if (remoteUrl.startsWith("git@ssh.dev.azure.com:")) {
228
+ const [, pathPart] = remoteUrl.split(":", 2);
229
+ if (pathPart) {
230
+ const segments = pathPart.split("/").filter(Boolean);
231
+ // Format: v3/{organization}/{project}/{repo}
232
+ if (segments.length >= 2) {
233
+ return segments[1];
234
+ }
235
+ }
236
+ }
237
+ return undefined;
238
+ }
239
+ function isGitRemoteMissingError(error) {
240
+ if (!error || typeof error !== "object") {
241
+ return false;
242
+ }
243
+ const execError = error;
244
+ if (typeof execError.stderr === "string") {
245
+ return execError.stderr.includes("No such remote");
246
+ }
247
+ return false;
248
+ }
249
+ function isGitCommandNotFoundError(error) {
250
+ return Boolean(error && typeof error === "object" && error.code === "ENOENT");
251
+ }
134
252
  async function promptQuestion(message) {
135
253
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
136
254
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uns-kit/cli",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "description": "Command line scaffolding tool for UNS applications",
5
5
  "type": "module",
6
6
  "license": "MIT",