Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions extractors/cds/tools/dist/cds-extractor.bundle.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions extractors/cds/tools/dist/cds-extractor.bundle.js.map

Large diffs are not rendered by default.

67 changes: 65 additions & 2 deletions extractors/cds/tools/src/packageManager/cacheInstaller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { execFileSync } from 'child_process';
import { createHash } from 'crypto';
import { existsSync, mkdirSync, writeFileSync } from 'fs';
import { join, resolve } from 'path';
import { copyFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
import { dirname, join, resolve } from 'path';

import type { CdsDependencyCombination } from './types';
import { CdsDependencyGraph, CdsProject } from '../cds/parser/types';
Expand Down Expand Up @@ -48,6 +48,61 @@ function addDependencyVersionWarning(
}
}

/**
* Find the nearest `.npmrc` file by searching the given directory and its
* ancestors up to (and including) the filesystem root. npm itself walks the
* directory tree when looking for project-level `.npmrc` files, so we mirror
* that behaviour here.
*
* @param startDir The directory from which to start the upward search.
* @returns The absolute path to the nearest `.npmrc`, or `undefined` if none is found.
*/
export function findNearestNpmrc(startDir: string): string | undefined {
let current = resolve(startDir);

// Walk up the directory tree until we find an .npmrc or reach the root

while (true) {
const candidate = join(current, '.npmrc');
if (existsSync(candidate)) {
return candidate;
}
const parent = dirname(current);
if (parent === current) {
// Reached filesystem root without finding .npmrc
return undefined;
}
current = parent;
}
}

/**
* Copy the project's `.npmrc` file (if any) into the cache directory so that
* `npm install` inside the cache respects custom registry configuration such
* as scoped registries (`@sap:registry=...`), authentication tokens, and
* `strict-ssl` settings.
*
* @param cacheDir The cache directory where dependencies will be installed.
* @param projectDir Absolute path to the project directory whose `.npmrc` should be used.
*/
export function copyNpmrcToCache(cacheDir: string, projectDir: string): void {
const npmrcPath = findNearestNpmrc(projectDir);
if (!npmrcPath) {
return;
}

const dest = join(cacheDir, '.npmrc');
try {
copyFileSync(npmrcPath, dest);
cdsExtractorLog('info', `Copied .npmrc from '${npmrcPath}' to cache directory '${cacheDir}'`);
} catch (err) {
cdsExtractorLog(
'warn',
`Failed to copy .npmrc to cache directory: ${err instanceof Error ? err.message : String(err)}`,
);
}
}
Comment thread
data-douser marked this conversation as resolved.

/**
* Install dependencies for CDS projects using a robust cache strategy with fallback logic
* @param dependencyGraph The dependency graph of the project
Expand Down Expand Up @@ -206,6 +261,14 @@ export function cacheInstallDependencies(
}
}

// Ensure the cache directory has an .npmrc that reflects the projects' registry configuration
const npmrcProjectDir = Array.from(dependencyGraph.projects.values())
.map(project => project.projectDir)
.find(projectDir => projectDir && existsSync(join(sourceRoot, projectDir, '.npmrc')));
if (npmrcProjectDir) {
copyNpmrcToCache(cacheDir, join(sourceRoot, npmrcProjectDir));
}

// Try to install dependencies in the cache directory
// Get the first project package.json path for diagnostic purposes
const samplePackageJsonPath = Array.from(dependencyGraph.projects.values()).find(
Expand Down
2 changes: 1 addition & 1 deletion extractors/cds/tools/src/packageManager/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Export the new robust installer functionality (preferred)
export { cacheInstallDependencies } from './cacheInstaller';
export { cacheInstallDependencies, copyNpmrcToCache, findNearestNpmrc } from './cacheInstaller';
export { needsFullDependencyInstallation, projectInstallDependencies } from './projectInstaller';
export type { CdsDependencyCombination } from './types';
export {
Expand Down
Loading
Loading