Skip to content

Commit 2dab201

Browse files
authored
report cdk versions (#2414)
* report cdk versions * update how and when we read lock file * update lock file reader factory * try this * update eol for windows * refactor lock file readers * PR feedback * fix lint
1 parent 22a2863 commit 2dab201

34 files changed

+1025
-17
lines changed

.changeset/silver-tables-do.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@aws-amplify/backend-deployer': patch
3+
'create-amplify': patch
4+
'@aws-amplify/backend-cli': patch
5+
'@aws-amplify/cli-core': patch
6+
'@aws-amplify/platform-core': minor
7+
'@aws-amplify/plugin-types': minor
8+
---
9+
10+
Report cdk versions

.eslint_dictionary.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"changelog",
2525
"changeset",
2626
"changesets",
27+
"checksum",
2728
"chown",
2829
"claude",
2930
"cloudformation",
@@ -99,6 +100,7 @@
99100
"lang",
100101
"linux",
101102
"localhost",
103+
"lockfile",
102104
"lsof",
103105
"lstat",
104106
"macos",
@@ -167,6 +169,7 @@
167169
"subpath",
168170
"syncable",
169171
"synthing",
172+
"testapp",
170173
"testname",
171174
"testnamebucket",
172175
"testuser",

packages/backend-deployer/src/cdk_deployer.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ void describe('invokeCDKCommand', () => {
4646
runWithPackageManager: mock.fn(() => Promise.resolve() as never),
4747
getCommand: (args: string[]) => `'npx ${args.join(' ')}'`,
4848
allowsSignalPropagation: () => true,
49+
tryGetDependencies: mock.fn(() => Promise.resolve([])),
4950
};
5051

5152
const invoker = new CDKDeployer(

packages/cli-core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"@aws-amplify/platform-core": "^1.3.0",
2323
"@inquirer/prompts": "^3.0.0",
2424
"execa": "^9.5.1",
25-
"kleur": "^4.1.5"
25+
"kleur": "^4.1.5",
26+
"zod": "^3.22.2"
2627
}
2728
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import assert from 'assert';
2+
import fsp from 'fs/promises';
3+
import { afterEach, describe, it, mock } from 'node:test';
4+
import path from 'path';
5+
import { NpmLockFileReader } from './npm_lock_file_reader.js';
6+
7+
void describe('NpmLockFileReader', () => {
8+
const fspReadFileMock = mock.method(fsp, 'readFile', () =>
9+
JSON.stringify({
10+
name: 'test_project',
11+
version: '1.0.0',
12+
packages: {
13+
'': {
14+
name: 'test_project',
15+
version: '1.0.0',
16+
},
17+
'node_modules/test_dep': {
18+
version: '1.2.3',
19+
},
20+
'node_modules/some_other_dep': {
21+
version: '12.13.14',
22+
},
23+
},
24+
})
25+
);
26+
const npmLockFileReader = new NpmLockFileReader();
27+
28+
afterEach(() => {
29+
fspReadFileMock.mock.resetCalls();
30+
});
31+
32+
void it('can get lock file contents from cwd', async () => {
33+
const lockFileContents =
34+
await npmLockFileReader.getLockFileContentsFromCwd();
35+
const expectedLockFileContents = {
36+
dependencies: [
37+
{
38+
name: 'test_dep', // "node_modules/" prefix is removed
39+
version: '1.2.3',
40+
},
41+
{
42+
name: 'some_other_dep', // "node_modules/" prefix is removed
43+
version: '12.13.14',
44+
},
45+
],
46+
};
47+
assert.deepEqual(lockFileContents, expectedLockFileContents);
48+
assert.strictEqual(
49+
fspReadFileMock.mock.calls[0].arguments[0],
50+
path.resolve(process.cwd(), 'package-lock.json')
51+
);
52+
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
53+
});
54+
55+
void it('returns undefined when package-lock.json is not present or parse-able', async () => {
56+
fspReadFileMock.mock.mockImplementationOnce(() =>
57+
Promise.reject(new Error())
58+
);
59+
const lockFileContents =
60+
await npmLockFileReader.getLockFileContentsFromCwd();
61+
assert.deepEqual(lockFileContents, undefined);
62+
});
63+
64+
void it('returns empty dependency array when package-lock.json does not have dependencies', async () => {
65+
fspReadFileMock.mock.mockImplementationOnce(() =>
66+
JSON.stringify({
67+
name: 'test_project',
68+
version: '1.0.0',
69+
})
70+
);
71+
const lockFileContents =
72+
await npmLockFileReader.getLockFileContentsFromCwd();
73+
assert.deepEqual(lockFileContents, { dependencies: [] });
74+
});
75+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { Dependency } from '@aws-amplify/plugin-types';
2+
import fsp from 'fs/promises';
3+
import path from 'path';
4+
import z from 'zod';
5+
import { LockFileContents, LockFileReader } from './types.js';
6+
import { printer } from '../../printer.js';
7+
import { LogLevel } from '../../printer/printer.js';
8+
9+
/**
10+
* NpmLockFileReader is an abstraction around the logic used to read and parse lock file contents
11+
*/
12+
export class NpmLockFileReader implements LockFileReader {
13+
getLockFileContentsFromCwd = async (): Promise<
14+
LockFileContents | undefined
15+
> => {
16+
const dependencies: Array<Dependency> = [];
17+
const packageLockJsonPath = path.resolve(
18+
process.cwd(),
19+
'package-lock.json'
20+
);
21+
let packageLockJson;
22+
try {
23+
const jsonLockContents = await fsp.readFile(packageLockJsonPath, 'utf-8');
24+
const jsonLockParsedValue = JSON.parse(jsonLockContents);
25+
// This will strip fields that are not part of the package lock schema
26+
packageLockJson = packageLockJsonSchema.parse(jsonLockParsedValue);
27+
} catch (error) {
28+
printer.log(
29+
`Failed to get lock file contents because ${packageLockJsonPath} does not exist or is not parse-able`,
30+
LogLevel.DEBUG
31+
);
32+
return;
33+
}
34+
35+
for (const key in packageLockJson.packages) {
36+
if (key === '') {
37+
// Skip root project in packages
38+
continue;
39+
}
40+
const dependencyVersion = packageLockJson.packages[key].version;
41+
42+
// Version may not exist if package is a symbolic link
43+
if (dependencyVersion) {
44+
// Remove "node_modules/" prefix
45+
const dependencyName = key.replace(/^node_modules\//, '');
46+
dependencies.push({
47+
name: dependencyName,
48+
version: dependencyVersion,
49+
});
50+
}
51+
}
52+
53+
return { dependencies };
54+
};
55+
}
56+
57+
const packageLockJsonSchema = z.object({
58+
packages: z
59+
.record(
60+
z.string(),
61+
z.object({
62+
version: z.string().optional(),
63+
})
64+
)
65+
.optional(),
66+
});
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import assert from 'assert';
2+
import fsp from 'fs/promises';
3+
import { afterEach, describe, it, mock } from 'node:test';
4+
import path from 'path';
5+
import { PnpmLockFileReader } from './pnpm_lock_file_reader.js';
6+
7+
void describe('PnpmLockFileReader', () => {
8+
const fspReadFileMock = mock.method(
9+
fsp,
10+
'readFile',
11+
() => `lockfileVersion: '9.0'
12+
13+
settings:
14+
autoInstallPeers: true
15+
excludeLinksFromLockfile: false
16+
17+
importers:
18+
19+
.:
20+
dependencies:
21+
aws-amplify:
22+
specifier: ^6.12.0
23+
version: 6.12.0
24+
devDependencies:
25+
'@aws-amplify/backend':
26+
specifier: ^1.11.0
27+
28+
'@aws-amplify/backend-cli':
29+
specifier: ^1.4.5
30+
31+
aws-cdk:
32+
specifier: ^2.173.4
33+
version: 2.174.1
34+
aws-cdk-lib:
35+
specifier: ^2.173.4
36+
version: 2.174.1([email protected])
37+
constructs:
38+
specifier: ^10.4.2
39+
version: 10.4.2
40+
esbuild:
41+
specifier: ^0.24.2
42+
version: 0.24.2
43+
tsx:
44+
specifier: ^4.19.2
45+
version: 4.19.2
46+
typescript:
47+
specifier: ^5.7.2
48+
version: 5.7.2
49+
50+
packages:
51+
52+
53+
resolution: {integrity: some-sha}
54+
engines: {node: '>=6.0.0'}
55+
56+
57+
resolution: {integrity: some-other-sha}
58+
engines: {node: '>=8'}`
59+
);
60+
const pnpmLockFileReader = new PnpmLockFileReader();
61+
62+
afterEach(() => {
63+
fspReadFileMock.mock.resetCalls();
64+
});
65+
66+
void it('can get lock file contents from cwd', async () => {
67+
const lockFileContents =
68+
await pnpmLockFileReader.getLockFileContentsFromCwd();
69+
const expectedLockFileContents = {
70+
dependencies: [
71+
{
72+
name: '@test_dep',
73+
version: '1.2.3',
74+
},
75+
{
76+
name: 'some_other_dep',
77+
version: '12.13.14',
78+
},
79+
],
80+
};
81+
assert.deepEqual(lockFileContents, expectedLockFileContents);
82+
assert.strictEqual(
83+
fspReadFileMock.mock.calls[0].arguments[0],
84+
path.resolve(process.cwd(), 'pnpm-lock.yaml')
85+
);
86+
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
87+
});
88+
89+
void it('returns empty lock file contents when pnpm-lock.yaml is not present or parse-able', async () => {
90+
fspReadFileMock.mock.mockImplementationOnce(() =>
91+
Promise.reject(new Error())
92+
);
93+
const lockFileContents =
94+
await pnpmLockFileReader.getLockFileContentsFromCwd();
95+
assert.deepEqual(lockFileContents, undefined);
96+
});
97+
98+
void it('returns empty dependency array when pnpm-lock.yaml does not have dependencies', async () => {
99+
mock.method(
100+
fsp,
101+
'readFile',
102+
() => `lockfileVersion: '9.0'
103+
104+
settings:
105+
autoInstallPeers: true
106+
excludeLinksFromLockfile: false
107+
108+
importers:
109+
110+
.:
111+
dependencies:
112+
aws-amplify:
113+
specifier: ^6.12.0
114+
version: 6.12.0
115+
devDependencies:
116+
'@aws-amplify/backend':
117+
specifier: ^1.11.0
118+
119+
'@aws-amplify/backend-cli':
120+
specifier: ^1.4.5
121+
122+
aws-cdk:
123+
specifier: ^2.173.4
124+
version: 2.174.1
125+
aws-cdk-lib:
126+
specifier: ^2.173.4
127+
version: 2.174.1([email protected])
128+
constructs:
129+
specifier: ^10.4.2
130+
version: 10.4.2
131+
esbuild:
132+
specifier: ^0.24.2
133+
version: 0.24.2
134+
tsx:
135+
specifier: ^4.19.2
136+
version: 4.19.2
137+
typescript:
138+
specifier: ^5.7.2
139+
version: 5.7.2`
140+
);
141+
const lockFileContents =
142+
await pnpmLockFileReader.getLockFileContentsFromCwd();
143+
assert.deepEqual(lockFileContents, { dependencies: [] });
144+
});
145+
});

0 commit comments

Comments
 (0)