@datacore-one/cli 1.0.7 → 1.0.9

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 +166 -76
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -8502,7 +8502,7 @@ function checkNode(platform2) {
8502
8502
  if (installed) {
8503
8503
  version = getVersion("node", "-v");
8504
8504
  const major = parseInt(version?.replace("v", "").split(".")[0] ?? "0", 10);
8505
- meetsMin = major >= 18;
8505
+ meetsMin = major >= 20;
8506
8506
  }
8507
8507
  return {
8508
8508
  name: "node",
@@ -9687,6 +9687,38 @@ function runArgs(cmd, args, opts) {
9687
9687
  return false;
9688
9688
  }
9689
9689
  }
9690
+ function runArgsWithError(cmd, args, opts) {
9691
+ try {
9692
+ execFileSync2(cmd, args, { stdio: "pipe", cwd: opts?.cwd, timeout: opts?.timeout });
9693
+ return { ok: true };
9694
+ } catch (err) {
9695
+ const stderr = err?.stderr?.toString().trim() || "";
9696
+ return { ok: false, stderr };
9697
+ }
9698
+ }
9699
+ function initGitInDir(repoUrl, dir, timeout = 300000) {
9700
+ let r = runArgsWithError("git", ["init"], { cwd: dir, timeout: 30000 });
9701
+ if (!r.ok)
9702
+ return r;
9703
+ r = runArgsWithError("git", ["remote", "add", "origin", repoUrl], { cwd: dir, timeout: 5000 });
9704
+ if (!r.ok) {
9705
+ r = runArgsWithError("git", ["remote", "set-url", "origin", repoUrl], { cwd: dir, timeout: 5000 });
9706
+ if (!r.ok)
9707
+ return r;
9708
+ }
9709
+ r = runArgsWithError("git", ["fetch", "origin"], { cwd: dir, timeout });
9710
+ if (!r.ok)
9711
+ return r;
9712
+ const headRef = runArgsOutput("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], { timeout: 5000 });
9713
+ const branch = headRef?.replace("refs/remotes/origin/", "") || "main";
9714
+ r = runArgsWithError("git", ["reset", `origin/${branch}`], { cwd: dir, timeout: 30000 });
9715
+ if (!r.ok)
9716
+ return r;
9717
+ runArgs("git", ["branch", "-M", branch], { cwd: dir });
9718
+ runArgs("git", ["branch", "--set-upstream-to", `origin/${branch}`], { cwd: dir });
9719
+ runArgs("git", ["checkout", "--", "."], { cwd: dir, timeout: 60000 });
9720
+ return { ok: true };
9721
+ }
9690
9722
  function runArgsOutput(cmd, args, opts) {
9691
9723
  try {
9692
9724
  return execFileSync2(cmd, args, { encoding: "utf-8", stdio: "pipe", timeout: opts?.timeout }).trim();
@@ -9870,7 +9902,7 @@ function isInitialized() {
9870
9902
  }
9871
9903
  }
9872
9904
  async function initDatacore(options = {}) {
9873
- const { nonInteractive = false, skipChecks = false, stream = false, verbose = false } = options;
9905
+ const { nonInteractive = false, skipChecks = false, stream = false, verbose = false, force = false } = options;
9874
9906
  const isTTY = stream && process.stdout.isTTY;
9875
9907
  const interactive = isTTY && !nonInteractive;
9876
9908
  const platform2 = detectPlatform();
@@ -10065,94 +10097,151 @@ async function initDatacore(options = {}) {
10065
10097
  console.log();
10066
10098
  }
10067
10099
  const ghAuth = checkGhAuth();
10068
- if (existsSync9(join10(DATA_DIR8, ".git"))) {
10100
+ const upstreamUrl = `https://github.com/${UPSTREAM_REPO}.git`;
10101
+ let ghUser;
10102
+ if (ghAuth.available) {
10103
+ ghUser = ghAuth.user || runArgsOutput("gh", ["api", "user", "-q", ".login"], { timeout: 15000 });
10104
+ const forkSpinner = isTTY ? new Spinner("Checking fork...") : null;
10105
+ forkSpinner?.start();
10106
+ const forkExists = runArgs("gh", ["repo", "view", `${ghUser}/datacore`], { cwd: process.env.HOME, timeout: 30000 });
10107
+ if (!forkExists) {
10108
+ forkSpinner?.update("Forking repository...");
10109
+ const forked = runArgs("gh", ["repo", "fork", UPSTREAM_REPO, "--clone=false"], { timeout: 30000 });
10110
+ if (forked) {
10111
+ forkSpinner?.succeed(`Forked to ${ghUser}/datacore`);
10112
+ } else {
10113
+ forkSpinner?.fail("Fork failed (will clone upstream directly)");
10114
+ result.warnings.push("Could not fork repository - will clone directly");
10115
+ ghUser = undefined;
10116
+ }
10117
+ } else {
10118
+ forkSpinner?.succeed(`Fork exists: ${ghUser}/datacore`);
10119
+ }
10120
+ }
10121
+ const cloneUrls = [];
10122
+ if (ghUser) {
10123
+ cloneUrls.push(`https://github.com/${ghUser}/datacore.git`);
10124
+ cloneUrls.push(`git@github.com:${ghUser}/datacore.git`);
10125
+ }
10126
+ if (!ghAuth.available && interactive) {
10127
+ const customUrl = await prompt(" Repository URL", upstreamUrl);
10128
+ cloneUrls.push(customUrl);
10129
+ const sshUrl = customUrl.replace("https://github.com/", "git@github.com:");
10130
+ if (sshUrl !== customUrl)
10131
+ cloneUrls.push(sshUrl);
10132
+ } else {
10133
+ cloneUrls.push(upstreamUrl);
10134
+ cloneUrls.push(`git@github.com:${UPSTREAM_REPO}.git`);
10135
+ }
10136
+ if (!ghAuth.available && interactive) {
10137
+ console.log(` ${c2.dim}GitHub CLI not authenticated. Cloning upstream directly.${c2.reset}`);
10138
+ console.log(` ${c2.dim}You can fork later with: gh repo fork --remote${c2.reset}`);
10139
+ console.log();
10140
+ }
10141
+ const hasGitDir = existsSync9(join10(DATA_DIR8, ".git"));
10142
+ const hasValidGit = hasGitDir && runArgs("git", ["rev-parse", "HEAD"], { cwd: DATA_DIR8, timeout: 5000 });
10143
+ if (hasValidGit) {
10069
10144
  if (isTTY)
10070
10145
  console.log(` ${c2.green}✓${c2.reset} Found existing repository at ~/Data`);
10071
- const spinner = isTTY ? new Spinner("Pulling latest changes...") : null;
10072
- spinner?.start();
10146
+ const pullSpinner = isTTY ? new Spinner("Pulling latest changes...") : null;
10147
+ pullSpinner?.start();
10073
10148
  if (runArgs("git", ["pull", "--rebase", "--autostash"], { cwd: DATA_DIR8, timeout: 60000 })) {
10074
- spinner?.succeed("Repository up to date");
10149
+ pullSpinner?.succeed("Repository up to date");
10075
10150
  } else {
10076
- spinner?.fail("Pull failed (non-fatal, continuing)");
10151
+ pullSpinner?.fail("Pull failed (non-fatal, continuing)");
10077
10152
  result.warnings.push("Could not pull latest changes");
10078
10153
  }
10079
- } else if (!existsSync9(DATA_DIR8) || readdirSync3(DATA_DIR8).filter((f) => !f.startsWith(".")).length === 0) {
10080
- if (ghAuth.available) {
10081
- const spinner = isTTY ? new Spinner("Forking repository...") : null;
10082
- spinner?.start();
10083
- const ghUser = ghAuth.user || runArgsOutput("gh", ["api", "user", "-q", ".login"], { timeout: 15000 });
10084
- const forkExists = runArgs("gh", ["repo", "view", `${ghUser}/datacore`], { cwd: process.env.HOME, timeout: 30000 });
10085
- if (!forkExists) {
10086
- const forked = runArgs("gh", ["repo", "fork", UPSTREAM_REPO, "--clone=false"], { timeout: 30000 });
10087
- if (forked) {
10088
- spinner?.succeed(`Forked to ${ghUser}/datacore`);
10089
- } else {
10090
- spinner?.fail("Fork failed");
10091
- result.warnings.push("Could not fork repository - will clone directly");
10154
+ const currentUpstream = runArgsOutput("git", ["remote", "get-url", "upstream"]);
10155
+ if (!currentUpstream) {
10156
+ runArgs("git", ["remote", "add", "upstream", upstreamUrl], { cwd: DATA_DIR8 });
10157
+ }
10158
+ } else {
10159
+ mkdirSync6(DATA_DIR8, { recursive: true });
10160
+ const cloneSpinner = isTTY ? new Spinner("Cloning into ~/Data...") : null;
10161
+ cloneSpinner?.start();
10162
+ let lastCloneErr = "";
10163
+ let cloned = false;
10164
+ for (const url of cloneUrls) {
10165
+ const r = runArgsWithError("git", ["clone", url, "."], { cwd: DATA_DIR8, timeout: 300000 });
10166
+ if (r.ok) {
10167
+ cloned = true;
10168
+ break;
10169
+ }
10170
+ lastCloneErr = r.stderr || "";
10171
+ if (lastCloneErr.includes("already exists and is not an empty directory"))
10172
+ break;
10173
+ }
10174
+ if (cloned) {
10175
+ cloneSpinner?.succeed("Cloned into ~/Data");
10176
+ result.created.push(DATA_DIR8);
10177
+ if (ghUser) {
10178
+ runArgs("git", ["remote", "add", "upstream", upstreamUrl], { cwd: DATA_DIR8 });
10179
+ }
10180
+ } else if (lastCloneErr.includes("already exists and is not an empty directory")) {
10181
+ if (!force) {
10182
+ cloneSpinner?.fail("~/Data is not empty");
10183
+ const entries = readdirSync3(DATA_DIR8);
10184
+ if (isTTY) {
10185
+ console.log(` ${c2.dim}~/Data contains ${entries.length} items but is not a git repository.${c2.reset}`);
10186
+ console.log(` ${c2.dim}Use --force to initialize git in the existing directory.${c2.reset}`);
10187
+ console.log(` ${c2.dim}Or remove/rename ~/Data for a fresh install.${c2.reset}`);
10092
10188
  }
10093
- } else {
10094
- spinner?.succeed(`Fork exists: ${ghUser}/datacore`);
10095
- }
10096
- const cloneSpinner = isTTY ? new Spinner("Cloning into ~/Data...") : null;
10097
- cloneSpinner?.start();
10098
- mkdirSync6(DATA_DIR8, { recursive: true });
10099
- const cloneUrl = `https://github.com/${ghUser}/datacore.git`;
10100
- let cloned = runArgs("git", ["clone", cloneUrl, "."], { cwd: DATA_DIR8, timeout: 300000 });
10101
- if (!cloned) {
10102
- cloned = runArgs("git", ["clone", `git@github.com:${ghUser}/datacore.git`, "."], { cwd: DATA_DIR8, timeout: 300000 });
10103
- }
10104
- if (!cloned) {
10105
- cloned = runArgs("git", ["clone", `https://github.com/${UPSTREAM_REPO}.git`, "."], { cwd: DATA_DIR8, timeout: 300000 });
10106
- if (!cloned) {
10107
- cloned = runArgs("git", ["clone", `git@github.com:${UPSTREAM_REPO}.git`, "."], { cwd: DATA_DIR8, timeout: 300000 });
10189
+ result.errors.push("~/Data exists and is not empty. Use --force to re-initialize.");
10190
+ op.failStep("clone_repo", "Directory not empty");
10191
+ op.fail("~/Data is not empty");
10192
+ return result;
10193
+ }
10194
+ cloneSpinner?.update("Initializing git in existing ~/Data...");
10195
+ let initOk = false;
10196
+ let lastInitErr = "";
10197
+ for (const url of cloneUrls) {
10198
+ const r = initGitInDir(url, DATA_DIR8);
10199
+ if (r.ok) {
10200
+ initOk = true;
10201
+ break;
10108
10202
  }
10203
+ lastInitErr = r.stderr || "";
10109
10204
  }
10110
- if (cloned) {
10111
- cloneSpinner?.succeed("Cloned into ~/Data");
10112
- result.created.push(DATA_DIR8);
10113
- runArgs("git", ["remote", "add", "upstream", `https://github.com/${UPSTREAM_REPO}.git`], { cwd: DATA_DIR8 });
10205
+ if (initOk) {
10206
+ cloneSpinner?.succeed("Git initialized in existing ~/Data");
10207
+ if (ghUser) {
10208
+ runArgs("git", ["remote", "add", "upstream", upstreamUrl], { cwd: DATA_DIR8 });
10209
+ }
10114
10210
  } else {
10115
- cloneSpinner?.fail("Clone failed");
10116
- result.errors.push("Could not clone repository");
10117
- op.failStep("clone_repo", "Clone failed");
10118
- op.fail("Clone failed");
10211
+ cloneSpinner?.fail("Could not initialize git");
10212
+ result.errors.push(`Git init failed: ${lastInitErr}`);
10213
+ if (lastInitErr.includes("Authentication") || lastInitErr.includes("403") || lastInitErr.includes("401")) {
10214
+ if (isTTY)
10215
+ console.log(` ${c2.dim}Check your GitHub access: gh auth status${c2.reset}`);
10216
+ }
10217
+ op.failStep("clone_repo", "Git init failed");
10218
+ op.fail("Git init failed");
10119
10219
  return result;
10120
10220
  }
10121
10221
  } else {
10122
- let repoUrl;
10123
- if (interactive) {
10124
- console.log(` ${c2.dim}GitHub CLI not authenticated. Cloning upstream directly.${c2.reset}`);
10125
- console.log(` ${c2.dim}You can fork later with: gh repo fork --remote${c2.reset}`);
10126
- console.log();
10127
- repoUrl = await prompt(" Repository URL", `https://github.com/${UPSTREAM_REPO}.git`);
10128
- } else {
10129
- repoUrl = `https://github.com/${UPSTREAM_REPO}.git`;
10130
- }
10131
- mkdirSync6(DATA_DIR8, { recursive: true });
10132
- const spinner = isTTY ? new Spinner("Cloning into ~/Data...") : null;
10133
- spinner?.start();
10134
- let cloned = runArgs("git", ["clone", repoUrl, "."], { cwd: DATA_DIR8, timeout: 300000 });
10135
- if (!cloned) {
10136
- const sshUrl = repoUrl.replace("https://github.com/", "git@github.com:");
10137
- cloned = runArgs("git", ["clone", sshUrl, "."], { cwd: DATA_DIR8, timeout: 300000 });
10138
- }
10139
- if (cloned) {
10140
- spinner?.succeed("Cloned into ~/Data");
10141
- result.created.push(DATA_DIR8);
10222
+ cloneSpinner?.fail("Clone failed");
10223
+ if (lastCloneErr.includes("not found") || lastCloneErr.includes("not exist") || lastCloneErr.includes("does not appear to be a git repository")) {
10224
+ result.errors.push("Repository not found");
10225
+ if (isTTY) {
10226
+ if (ghUser) {
10227
+ console.log(` ${c2.dim}Neither ${ghUser}/datacore nor ${UPSTREAM_REPO} could be cloned.${c2.reset}`);
10228
+ }
10229
+ console.log(` ${c2.dim}Ensure you have access to ${UPSTREAM_REPO} and try again.${c2.reset}`);
10230
+ console.log(` ${c2.dim}Check access: gh repo view ${UPSTREAM_REPO}${c2.reset}`);
10231
+ }
10232
+ } else if (lastCloneErr.includes("Authentication") || lastCloneErr.includes("Permission") || lastCloneErr.includes("403") || lastCloneErr.includes("401")) {
10233
+ result.errors.push("Authentication failed");
10234
+ if (isTTY) {
10235
+ console.log(` ${c2.dim}Could not authenticate with GitHub.${c2.reset}`);
10236
+ console.log(` ${c2.dim}Try: gh auth login${c2.reset}`);
10237
+ }
10142
10238
  } else {
10143
- spinner?.fail("Clone failed");
10144
- result.errors.push(`Could not clone from ${repoUrl}`);
10145
- op.failStep("clone_repo", "Clone failed");
10146
- op.fail("Clone failed");
10147
- return result;
10239
+ result.errors.push(`Clone failed: ${lastCloneErr}`);
10148
10240
  }
10241
+ op.failStep("clone_repo", "Clone failed");
10242
+ op.fail("Clone failed");
10243
+ return result;
10149
10244
  }
10150
- } else {
10151
- if (isTTY) {
10152
- console.log(` ${c2.yellow}⚠${c2.reset} ~/Data exists but is not a git repository`);
10153
- console.log(` ${c2.dim}Expected a cloned Datacore repo. Some features may not work.${c2.reset}`);
10154
- }
10155
- result.warnings.push(`${DATA_DIR8} is not a git repository`);
10156
10245
  }
10157
10246
  const dipsDir = join10(DATACORE_DIR, "dips");
10158
10247
  if (existsSync9(DATACORE_DIR) && !existsSync9(join10(dipsDir, ".git"))) {
@@ -10924,7 +11013,7 @@ async function initDatacore(options = {}) {
10924
11013
  }
10925
11014
 
10926
11015
  // src/index.ts
10927
- var VERSION = "1.0.7";
11016
+ var VERSION = "1.0.9";
10928
11017
  var args = process.argv.slice(2);
10929
11018
  var parsed = parseArgs(args);
10930
11019
  async function handleMeta(command, cmdArgs, flags, format) {
@@ -10948,7 +11037,8 @@ async function handleMeta(command, cmdArgs, flags, format) {
10948
11037
  nonInteractive: flags.yes === true,
10949
11038
  skipChecks: flags["skip-checks"] === true,
10950
11039
  stream: format === "human",
10951
- verbose: flags.verbose === true
11040
+ verbose: flags.verbose === true,
11041
+ force: flags.force === true
10952
11042
  });
10953
11043
  if (format === "json") {
10954
11044
  output(result, format);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datacore-one/cli",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "CLI for setting up and managing Datacore installations",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,7 +20,7 @@
20
20
  "prepublishOnly": "bun run build"
21
21
  },
22
22
  "engines": {
23
- "node": ">=18"
23
+ "node": ">=20"
24
24
  },
25
25
  "keywords": [
26
26
  "datacore",