@bobfrankston/npmglobalize 1.0.98 → 1.0.100

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/lib.js +72 -9
  2. package/package.json +1 -1
package/lib.js CHANGED
@@ -116,6 +116,27 @@ function repairGitIndex(cwd) {
116
116
  return false;
117
117
  }
118
118
  }
119
+ /** Parse file paths from git add "Permission denied" / "unable to index file" errors.
120
+ * Matches lines like: error: open("data/PingDB.mdf"): Permission denied
121
+ * and: error: unable to index file 'data/PingDB.mdf' */
122
+ function parseDeniedFiles(errText) {
123
+ const files = [];
124
+ for (const line of errText.split('\n')) {
125
+ // error: open("path"): Permission denied
126
+ let m = line.match(/error:\s*open\(["'](.+?)["']\):\s*Permission denied/i);
127
+ if (m) {
128
+ files.push(m[1]);
129
+ continue;
130
+ }
131
+ // error: unable to index file 'path'
132
+ m = line.match(/error:\s*unable to index file\s+['"](.+?)['"]/i);
133
+ if (m) {
134
+ files.push(m[1]);
135
+ continue;
136
+ }
137
+ }
138
+ return files;
139
+ }
119
140
  /** Get npm command for current platform (npm.cmd on Windows, npm elsewhere) */
120
141
  function getNpmCommand() {
121
142
  return process.platform === 'win32' ? 'npm.cmd' : 'npm';
@@ -878,7 +899,7 @@ export function fixVersionTagMismatch(cwd, pkg, verbose = false) {
878
899
  return deletedAny;
879
900
  }
880
901
  /** Wait for a package version to appear on the npm registry */
881
- function waitForNpmVersion(pkgName, version, maxWaitMs = 60000) {
902
+ function waitForNpmVersion(pkgName, version, maxWaitMs = 90000) {
882
903
  const interval = 3000;
883
904
  const maxAttempts = Math.ceil(maxWaitMs / interval);
884
905
  process.stdout.write(`Waiting for ${pkgName}@${version} on npm registry`);
@@ -898,6 +919,16 @@ function waitForNpmVersion(pkgName, version, maxWaitMs = 60000) {
898
919
  process.stdout.write(' timed out\n');
899
920
  return false;
900
921
  }
922
+ /** Run npm install -g with retries for registry propagation delay */
923
+ function installGlobalWithRetry(pkgSpec, cwd, maxRetries = 3) {
924
+ let result = runCommand('npm', ['install', '-g', pkgSpec], { cwd, silent: false });
925
+ for (let attempt = 1; attempt < maxRetries && !result.success; attempt++) {
926
+ console.log(colors.yellow(` Retrying install (attempt ${attempt + 1}/${maxRetries}) in 10 seconds...`));
927
+ spawnSafe(process.platform === 'win32' ? 'timeout' : 'sleep', process.platform === 'win32' ? ['/t', '10', '/nobreak'] : ['10'], { stdio: 'pipe', shell: process.platform === 'win32' });
928
+ result = runCommand('npm', ['install', '-g', pkgSpec], { cwd, silent: false });
929
+ }
930
+ return result;
931
+ }
901
932
  /** Run a command and return success status */
902
933
  export function runCommand(cmd, args, options = {}) {
903
934
  const { silent = false, cwd } = options;
@@ -1265,7 +1296,10 @@ const RECOMMENDED_GITIGNORE = [
1265
1296
  '.globalize.json5',
1266
1297
  '*.log',
1267
1298
  '.DS_Store',
1268
- 'Thumbs.db'
1299
+ 'Thumbs.db',
1300
+ '*.mdf',
1301
+ '*.ldf',
1302
+ '*.ndf'
1269
1303
  ];
1270
1304
  /** Recommended .npmignore patterns */
1271
1305
  const RECOMMENDED_NPMIGNORE = [
@@ -2537,7 +2571,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
2537
2571
  else if (install) {
2538
2572
  waitForNpmVersion(pkgName, pkgVersion);
2539
2573
  console.log(`Installing ${pkgName}@${pkgVersion} globally from registry...`);
2540
- const registryInstallResult = runCommand('npm', ['install', '-g', `${pkgName}@${pkgVersion}`], { cwd, silent: false });
2574
+ const registryInstallResult = installGlobalWithRetry(`${pkgName}@${pkgVersion}`, cwd);
2541
2575
  if (registryInstallResult.success) {
2542
2576
  console.log(colors.green(`✓ Installed globally: ${pkgName}@${pkgVersion}`));
2543
2577
  }
@@ -2578,13 +2612,42 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
2578
2612
  if (nulCount > 0 && verbose) {
2579
2613
  console.log(` Removed ${nulCount} 'nul' file(s) (Windows reserved name)`);
2580
2614
  }
2581
- const addResult = runCommand('git', ['add', '-A'], { cwd });
2615
+ let addResult = runCommand('git', ['add', '-A'], { cwd, silent: true });
2582
2616
  if (!addResult.success) {
2583
- console.error(colors.red('ERROR: Failed to add files to git:'), addResult.stderr);
2584
- if (!force) {
2585
- return false;
2617
+ const errText = addResult.stderr + addResult.output;
2618
+ const deniedFiles = parseDeniedFiles(errText);
2619
+ if (deniedFiles.length > 0) {
2620
+ console.error(colors.red(`git add failed — ${deniedFiles.length} file(s) locked/permission denied:`));
2621
+ for (const f of deniedFiles) {
2622
+ console.error(colors.red(` ${f}`));
2623
+ }
2624
+ const ok = await confirm('Add these files to .gitignore and retry?', true);
2625
+ if (ok) {
2626
+ const gitignorePath = path.join(cwd, '.gitignore');
2627
+ let gitignoreContent = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
2628
+ if (gitignoreContent && !gitignoreContent.endsWith('\n'))
2629
+ gitignoreContent += '\n';
2630
+ for (const f of deniedFiles) {
2631
+ gitignoreContent += f + '\n';
2632
+ }
2633
+ fs.writeFileSync(gitignorePath, gitignoreContent);
2634
+ console.log(colors.green('✓ Updated .gitignore'));
2635
+ addResult = runCommand('git', ['add', '-A'], { cwd, silent: true });
2636
+ if (addResult.success) {
2637
+ console.log(colors.green('✓ git add succeeded after updating .gitignore'));
2638
+ }
2639
+ }
2640
+ if (!addResult.success) {
2641
+ return false;
2642
+ }
2643
+ }
2644
+ else {
2645
+ console.error(colors.red('ERROR: Failed to add files to git:'), errText);
2646
+ if (!force) {
2647
+ return false;
2648
+ }
2649
+ console.log(colors.yellow('Continuing with --force...'));
2586
2650
  }
2587
- console.log(colors.yellow('Continuing with --force...'));
2588
2651
  }
2589
2652
  let commitResult = runCommand('git', ['commit', '-m', commitMsg], { cwd });
2590
2653
  if (!commitResult.success) {
@@ -3008,7 +3071,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3008
3071
  console.log(`Installing globally from registry: ${pkgName}@${pkgVersion}...`);
3009
3072
  if (!dryRun) {
3010
3073
  waitForNpmVersion(pkgName, pkgVersion);
3011
- const installResult = runCommand('npm', ['install', '-g', `${pkgName}@${pkgVersion}`], { cwd, silent: false });
3074
+ const installResult = installGlobalWithRetry(`${pkgName}@${pkgVersion}`, cwd);
3012
3075
  if (installResult.success) {
3013
3076
  console.log(colors.green(`✓ Installed globally: ${pkgName}@${pkgVersion}`));
3014
3077
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.98",
3
+ "version": "1.0.100",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",