Skip to content

Commit 3e8d8d8

Browse files
authored
fix(cli): user agent is reported as undefined/undefined (#24663)
Our CLI's user agent is reported as `undefined/undefined`. This is because we are reading the package name and version from the CLI's `package.json` by using a relative path to the source file (using `__dirname`). However, since a good long while, our production CLI is being bundled using `esbuild` into a single JavaScript file. This means that at runtime, `__dirname` points to a completely different directory than the one it's been coded against, and so reading the `package.json` fails. Account for this by using a function that searches for `package.json`; still do it defensively so that if some other condition we didn't predict causes the search to fail, our CLI doesn't fail. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 160ac48 commit 3e8d8d8

File tree

3 files changed

+37
-8
lines changed

3 files changed

+37
-8
lines changed

packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { cached } from './cached';
1010
import { CredentialPlugins } from './credential-plugins';
1111
import { Mode } from './credentials';
1212
import { ISDK, SDK, isUnrecoverableAwsError } from './sdk';
13+
import { rootDir } from '../../util/directories';
1314
import { traceMethods } from '../../util/tracing';
1415

1516

@@ -417,9 +418,7 @@ function parseHttpOptions(options: SdkHttpOptions) {
417418

418419
let userAgent = options.userAgent;
419420
if (userAgent == null) {
420-
// Find the package.json from the main toolkit
421-
const pkg = JSON.parse(readIfPossible(path.join(__dirname, '..', '..', '..', 'package.json')) ?? '{}');
422-
userAgent = `${pkg.name}/${pkg.version}`;
421+
userAgent = defaultCliUserAgent();
423422
}
424423
config.customUserAgent = userAgent;
425424

@@ -444,6 +443,20 @@ function parseHttpOptions(options: SdkHttpOptions) {
444443
return config;
445444
}
446445

446+
/**
447+
* Find the package.json from the main toolkit.
448+
*
449+
* If we can't read it for some reason, try to do something reasonable anyway.
450+
* Fall back to argv[1], or a standard string if that is undefined for some reason.
451+
*/
452+
export function defaultCliUserAgent() {
453+
const root = rootDir(false);
454+
const pkg = JSON.parse((root ? readIfPossible(path.join(root, 'package.json')) : undefined) ?? '{}');
455+
const name = pkg.name ?? path.basename(process.argv[1] ?? 'cdk-cli');
456+
const version = pkg.version ?? '<unknown>';
457+
return `${name}/${version}`;
458+
}
459+
447460
/**
448461
* Find and return a CA certificate bundle path to be passed into the SDK.
449462
*/

packages/aws-cdk/lib/util/directories.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,27 @@ export function cdkCacheDir() {
2828
return path.join(cdkHomeDir(), 'cache');
2929
}
3030

31-
export function rootDir() {
32-
33-
function _rootDir(dirname: string): string {
31+
/**
32+
* From the current file, find the directory that contains the CLI's package.json
33+
*
34+
* Can't use `__dirname` in production code, as the CLI will get bundled as it's
35+
* released and `__dirname` will refer to a different location in the `.ts` form
36+
* as it will in the final executing form.
37+
*/
38+
export function rootDir(): string;
39+
export function rootDir(fail: true): string;
40+
export function rootDir(fail: false): string | undefined;
41+
export function rootDir(fail?: boolean) {
42+
function _rootDir(dirname: string): string | undefined {
3443
const manifestPath = path.join(dirname, 'package.json');
3544
if (fs.existsSync(manifestPath)) {
3645
return dirname;
3746
}
3847
if (path.dirname(dirname) === dirname) {
39-
throw new Error('Unable to find package manifest');
48+
if (fail ?? true) {
49+
throw new Error('Unable to find package manifest');
50+
}
51+
return undefined;
4052
}
4153
return _rootDir(path.dirname(dirname));
4254
}

packages/aws-cdk/test/api/sdk-provider.test.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { ConfigurationOptions } from 'aws-sdk/lib/config-base';
55
import * as promptly from 'promptly';
66
import * as uuid from 'uuid';
77
import { FakeSts, RegisterRoleOptions, RegisterUserOptions } from './fake-sts';
8-
import { ISDK, Mode, SDK, SdkProvider } from '../../lib/api/aws-auth';
8+
import { ISDK, Mode, SDK, SdkProvider, defaultCliUserAgent } from '../../lib/api/aws-auth';
99
import { PluginHost } from '../../lib/api/plugin';
1010
import * as logging from '../../lib/logging';
1111
import * as bockfs from '../bockfs';
@@ -623,6 +623,10 @@ test('even when using a profile to assume another profile, STS calls goes throug
623623
expect(called).toEqual(true);
624624
});
625625

626+
test('default useragent is reasonable', () => {
627+
expect(defaultCliUserAgent()).toContain('aws-cdk/');
628+
});
629+
626630
/**
627631
* Use object hackery to get the credentials out of the SDK object
628632
*/

0 commit comments

Comments
 (0)