@dafish/gogo-meta 1.1.0 → 1.2.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/dist/cli.js CHANGED
@@ -6,6 +6,7 @@ import { dirname, join, basename } from 'path';
6
6
  import { mkdir, appendFile, access, writeFile, readFile, unlink, symlink } from 'fs/promises';
7
7
  import { z } from 'zod';
8
8
  import pc from 'picocolors';
9
+ import { homedir } from 'os';
9
10
 
10
11
  var __defProp = Object.defineProperty;
11
12
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -321,6 +322,23 @@ function listCommands(metaConfig) {
321
322
  command: normalizeCommand(config)
322
323
  }));
323
324
  }
325
+ async function addToGitignore(metaDir, entry) {
326
+ const gitignorePath = join(metaDir, ".gitignore");
327
+ if (await fileExists(gitignorePath)) {
328
+ const content = await readFile(gitignorePath, "utf-8");
329
+ const lines = content.split("\n").map((line) => line.trim());
330
+ if (lines.includes(entry)) {
331
+ return false;
332
+ }
333
+ const suffix = content.endsWith("\n") ? "" : "\n";
334
+ await appendFile(gitignorePath, `${suffix}${entry}
335
+ `);
336
+ } else {
337
+ await writeFile(gitignorePath, `${entry}
338
+ `, "utf-8");
339
+ }
340
+ return true;
341
+ }
324
342
  var symbols = {
325
343
  success: pc.green("\u2713"),
326
344
  error: pc.red("\u2717"),
@@ -635,6 +653,81 @@ function registerRunCommand(program) {
635
653
 
636
654
  // src/commands/git/clone.ts
637
655
  init_executor();
656
+
657
+ // src/core/ssh.ts
658
+ init_executor();
659
+ function extractSshHost(url) {
660
+ if (url.startsWith("https://") || url.startsWith("http://") || url.startsWith("file://")) {
661
+ return null;
662
+ }
663
+ const sshMatch = url.match(/^ssh:\/\/[^@]+@([^/:]+)/);
664
+ if (sshMatch) {
665
+ return sshMatch[1] ?? null;
666
+ }
667
+ const gitMatch = url.match(/^[^@]+@([^:]+):/);
668
+ if (gitMatch) {
669
+ return gitMatch[1] ?? null;
670
+ }
671
+ return null;
672
+ }
673
+ function extractUniqueSshHosts(urls) {
674
+ const hosts = /* @__PURE__ */ new Set();
675
+ for (const url of urls) {
676
+ const host = extractSshHost(url);
677
+ if (host) {
678
+ hosts.add(host);
679
+ }
680
+ }
681
+ return Array.from(hosts);
682
+ }
683
+ async function isHostKnown(host) {
684
+ const knownHostsPath = join(homedir(), ".ssh", "known_hosts");
685
+ try {
686
+ const content = await readFile(knownHostsPath, "utf-8");
687
+ const hostPatterns = [
688
+ new RegExp(`^${escapeRegex(host)}[,\\s]`, "m"),
689
+ new RegExp(`^\\[${escapeRegex(host)}\\]:\\d+[,\\s]`, "m")
690
+ ];
691
+ return hostPatterns.some((pattern) => pattern.test(content));
692
+ } catch {
693
+ return false;
694
+ }
695
+ }
696
+ function escapeRegex(str) {
697
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
698
+ }
699
+ function addHostKey(host) {
700
+ const knownHostsPath = join(homedir(), ".ssh", "known_hosts");
701
+ const result = executeSync(`ssh-keyscan -H "${host}" >> "${knownHostsPath}" 2>/dev/null`, {
702
+ cwd: process.cwd()
703
+ });
704
+ return result.exitCode === 0;
705
+ }
706
+ async function ensureSshHostsKnown(urls) {
707
+ const hosts = extractUniqueSshHosts(urls);
708
+ const added = [];
709
+ const failed = [];
710
+ if (hosts.length === 0) {
711
+ return { added, failed };
712
+ }
713
+ for (const host of hosts) {
714
+ const known = await isHostKnown(host);
715
+ if (!known) {
716
+ info(`Adding SSH host key for ${host}...`);
717
+ const success2 = addHostKey(host);
718
+ if (success2) {
719
+ success(`Added host key for ${host}`);
720
+ added.push(host);
721
+ } else {
722
+ error(`Failed to add host key for ${host}`);
723
+ failed.push(host);
724
+ }
725
+ }
726
+ }
727
+ return { added, failed };
728
+ }
729
+
730
+ // src/commands/git/clone.ts
638
731
  function extractRepoName(url) {
639
732
  const match = url.match(/\/([^/]+?)(\.git)?$/);
640
733
  return match?.[1] ?? "repo";
@@ -646,6 +739,7 @@ async function cloneCommand(url, options = {}) {
646
739
  if (await fileExists(targetDir)) {
647
740
  throw new Error(`Directory "${repoName}" already exists`);
648
741
  }
742
+ await ensureSshHostsKnown([url]);
649
743
  info(`Cloning meta repository: ${url}`);
650
744
  const cloneResult = await execute(`git clone "${url}" "${repoName}"`, { cwd });
651
745
  if (cloneResult.exitCode !== 0) {
@@ -666,6 +760,13 @@ async function cloneCommand(url, options = {}) {
666
760
  info("No child repositories defined in .gogo");
667
761
  return;
668
762
  }
763
+ const urls = projects.map(([, url2]) => url2);
764
+ const { failed: failedHosts } = await ensureSshHostsKnown(urls);
765
+ if (failedHosts.length > 0) {
766
+ warning(
767
+ `Could not verify SSH host keys for: ${failedHosts.join(", ")}. Clone may fail.`
768
+ );
769
+ }
669
770
  info(`Cloning ${projects.length} child repositories...`);
670
771
  let successCount = 0;
671
772
  let failCount = 0;
@@ -725,6 +826,13 @@ async function updateCommand(options = {}) {
725
826
  success("All repositories are already cloned");
726
827
  return;
727
828
  }
829
+ const urls = missing.map(([, url]) => url);
830
+ const { failed: failedHosts } = await ensureSshHostsKnown(urls);
831
+ if (failedHosts.length > 0) {
832
+ warning(
833
+ `Could not verify SSH host keys for: ${failedHosts.join(", ")}. Clone may fail.`
834
+ );
835
+ }
728
836
  info(`Cloning ${missing.length} missing repositories...`);
729
837
  let successCount = 0;
730
838
  let failCount = 0;
@@ -1017,7 +1125,11 @@ async function importCommand(folder, url, options = {}) {
1017
1125
  const config2 = await readMetaConfig(metaDir);
1018
1126
  const updatedConfig2 = addProject(config2, folder, url);
1019
1127
  await writeMetaConfig(metaDir, updatedConfig2);
1128
+ const added2 = await addToGitignore(metaDir, folder);
1020
1129
  success(`Registered project "${folder}" (not cloned)`);
1130
+ if (added2) {
1131
+ info(`Added ${folder} to .gitignore`);
1132
+ }
1021
1133
  info(`Run "gogo git update" to clone missing projects`);
1022
1134
  return;
1023
1135
  }
@@ -1035,11 +1147,8 @@ async function importCommand(folder, url, options = {}) {
1035
1147
  await writeMetaConfig(metaDir, updatedConfig);
1036
1148
  success(`Imported project "${folder}"`);
1037
1149
  }
1038
- const gitignorePath = join(metaDir, ".gitignore");
1039
- if (await fileExists(gitignorePath)) {
1040
- await appendFile(gitignorePath, `
1041
- ${folder}
1042
- `);
1150
+ const added = await addToGitignore(metaDir, folder);
1151
+ if (added) {
1043
1152
  info(`Added ${folder} to .gitignore`);
1044
1153
  }
1045
1154
  }