Skip to content

Commit

Permalink
report cdk versions (#2414)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
rtpascual authored Jan 10, 2025
1 parent 22a2863 commit 2dab201
Show file tree
Hide file tree
Showing 34 changed files with 1,025 additions and 17 deletions.
10 changes: 10 additions & 0 deletions .changeset/silver-tables-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@aws-amplify/backend-deployer': patch
'create-amplify': patch
'@aws-amplify/backend-cli': patch
'@aws-amplify/cli-core': patch
'@aws-amplify/platform-core': minor
'@aws-amplify/plugin-types': minor
---

Report cdk versions
3 changes: 3 additions & 0 deletions .eslint_dictionary.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"changelog",
"changeset",
"changesets",
"checksum",
"chown",
"claude",
"cloudformation",
Expand Down Expand Up @@ -99,6 +100,7 @@
"lang",
"linux",
"localhost",
"lockfile",
"lsof",
"lstat",
"macos",
Expand Down Expand Up @@ -167,6 +169,7 @@
"subpath",
"syncable",
"synthing",
"testapp",
"testname",
"testnamebucket",
"testuser",
Expand Down
1 change: 1 addition & 0 deletions packages/backend-deployer/src/cdk_deployer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ void describe('invokeCDKCommand', () => {
runWithPackageManager: mock.fn(() => Promise.resolve() as never),
getCommand: (args: string[]) => `'npx ${args.join(' ')}'`,
allowsSignalPropagation: () => true,
tryGetDependencies: mock.fn(() => Promise.resolve([])),
};

const invoker = new CDKDeployer(
Expand Down
3 changes: 2 additions & 1 deletion packages/cli-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@aws-amplify/platform-core": "^1.3.0",
"@inquirer/prompts": "^3.0.0",
"execa": "^9.5.1",
"kleur": "^4.1.5"
"kleur": "^4.1.5",
"zod": "^3.22.2"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import assert from 'assert';
import fsp from 'fs/promises';
import { afterEach, describe, it, mock } from 'node:test';
import path from 'path';
import { NpmLockFileReader } from './npm_lock_file_reader.js';

void describe('NpmLockFileReader', () => {
const fspReadFileMock = mock.method(fsp, 'readFile', () =>
JSON.stringify({
name: 'test_project',
version: '1.0.0',
packages: {
'': {
name: 'test_project',
version: '1.0.0',
},
'node_modules/test_dep': {
version: '1.2.3',
},
'node_modules/some_other_dep': {
version: '12.13.14',
},
},
})
);
const npmLockFileReader = new NpmLockFileReader();

afterEach(() => {
fspReadFileMock.mock.resetCalls();
});

void it('can get lock file contents from cwd', async () => {
const lockFileContents =
await npmLockFileReader.getLockFileContentsFromCwd();
const expectedLockFileContents = {
dependencies: [
{
name: 'test_dep', // "node_modules/" prefix is removed
version: '1.2.3',
},
{
name: 'some_other_dep', // "node_modules/" prefix is removed
version: '12.13.14',
},
],
};
assert.deepEqual(lockFileContents, expectedLockFileContents);
assert.strictEqual(
fspReadFileMock.mock.calls[0].arguments[0],
path.resolve(process.cwd(), 'package-lock.json')
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
});

void it('returns undefined when package-lock.json is not present or parse-able', async () => {
fspReadFileMock.mock.mockImplementationOnce(() =>
Promise.reject(new Error())
);
const lockFileContents =
await npmLockFileReader.getLockFileContentsFromCwd();
assert.deepEqual(lockFileContents, undefined);
});

void it('returns empty dependency array when package-lock.json does not have dependencies', async () => {
fspReadFileMock.mock.mockImplementationOnce(() =>
JSON.stringify({
name: 'test_project',
version: '1.0.0',
})
);
const lockFileContents =
await npmLockFileReader.getLockFileContentsFromCwd();
assert.deepEqual(lockFileContents, { dependencies: [] });
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Dependency } from '@aws-amplify/plugin-types';
import fsp from 'fs/promises';
import path from 'path';
import z from 'zod';
import { LockFileContents, LockFileReader } from './types.js';
import { printer } from '../../printer.js';
import { LogLevel } from '../../printer/printer.js';

/**
* NpmLockFileReader is an abstraction around the logic used to read and parse lock file contents
*/
export class NpmLockFileReader implements LockFileReader {
getLockFileContentsFromCwd = async (): Promise<
LockFileContents | undefined
> => {
const dependencies: Array<Dependency> = [];
const packageLockJsonPath = path.resolve(
process.cwd(),
'package-lock.json'
);
let packageLockJson;
try {
const jsonLockContents = await fsp.readFile(packageLockJsonPath, 'utf-8');
const jsonLockParsedValue = JSON.parse(jsonLockContents);
// This will strip fields that are not part of the package lock schema
packageLockJson = packageLockJsonSchema.parse(jsonLockParsedValue);
} catch (error) {
printer.log(
`Failed to get lock file contents because ${packageLockJsonPath} does not exist or is not parse-able`,
LogLevel.DEBUG
);
return;
}

for (const key in packageLockJson.packages) {
if (key === '') {
// Skip root project in packages
continue;
}
const dependencyVersion = packageLockJson.packages[key].version;

// Version may not exist if package is a symbolic link
if (dependencyVersion) {
// Remove "node_modules/" prefix
const dependencyName = key.replace(/^node_modules\//, '');
dependencies.push({
name: dependencyName,
version: dependencyVersion,
});
}
}

return { dependencies };
};
}

const packageLockJsonSchema = z.object({
packages: z
.record(
z.string(),
z.object({
version: z.string().optional(),
})
)
.optional(),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import assert from 'assert';
import fsp from 'fs/promises';
import { afterEach, describe, it, mock } from 'node:test';
import path from 'path';
import { PnpmLockFileReader } from './pnpm_lock_file_reader.js';

void describe('PnpmLockFileReader', () => {
const fspReadFileMock = mock.method(
fsp,
'readFile',
() => `lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
aws-amplify:
specifier: ^6.12.0
version: 6.12.0
devDependencies:
'@aws-amplify/backend':
specifier: ^1.11.0
version: 1.12.0(@aws-sdk/[email protected])(@aws-sdk/[email protected])(@aws-sdk/[email protected](@aws-sdk/[email protected]))(@aws-sdk/[email protected])([email protected]([email protected]))([email protected])([email protected])
'@aws-amplify/backend-cli':
specifier: ^1.4.5
version: 1.4.6(@aws-sdk/[email protected](@aws-sdk/[email protected]))(@aws-sdk/[email protected])(@aws-sdk/[email protected])([email protected]([email protected]))([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])
aws-cdk:
specifier: ^2.173.4
version: 2.174.1
aws-cdk-lib:
specifier: ^2.173.4
version: 2.174.1([email protected])
constructs:
specifier: ^10.4.2
version: 10.4.2
esbuild:
specifier: ^0.24.2
version: 0.24.2
tsx:
specifier: ^4.19.2
version: 4.19.2
typescript:
specifier: ^5.7.2
version: 5.7.2
packages:
'@[email protected]':
resolution: {integrity: some-sha}
engines: {node: '>=6.0.0'}
'[email protected]':
resolution: {integrity: some-other-sha}
engines: {node: '>=8'}`
);
const pnpmLockFileReader = new PnpmLockFileReader();

afterEach(() => {
fspReadFileMock.mock.resetCalls();
});

void it('can get lock file contents from cwd', async () => {
const lockFileContents =
await pnpmLockFileReader.getLockFileContentsFromCwd();
const expectedLockFileContents = {
dependencies: [
{
name: '@test_dep',
version: '1.2.3',
},
{
name: 'some_other_dep',
version: '12.13.14',
},
],
};
assert.deepEqual(lockFileContents, expectedLockFileContents);
assert.strictEqual(
fspReadFileMock.mock.calls[0].arguments[0],
path.resolve(process.cwd(), 'pnpm-lock.yaml')
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
});

void it('returns empty lock file contents when pnpm-lock.yaml is not present or parse-able', async () => {
fspReadFileMock.mock.mockImplementationOnce(() =>
Promise.reject(new Error())
);
const lockFileContents =
await pnpmLockFileReader.getLockFileContentsFromCwd();
assert.deepEqual(lockFileContents, undefined);
});

void it('returns empty dependency array when pnpm-lock.yaml does not have dependencies', async () => {
mock.method(
fsp,
'readFile',
() => `lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
aws-amplify:
specifier: ^6.12.0
version: 6.12.0
devDependencies:
'@aws-amplify/backend':
specifier: ^1.11.0
version: 1.12.0(@aws-sdk/[email protected])(@aws-sdk/[email protected])(@aws-sdk/[email protected](@aws-sdk/[email protected]))(@aws-sdk/[email protected])([email protected]([email protected]))([email protected])([email protected])
'@aws-amplify/backend-cli':
specifier: ^1.4.5
version: 1.4.6(@aws-sdk/[email protected](@aws-sdk/[email protected]))(@aws-sdk/[email protected])(@aws-sdk/[email protected])([email protected]([email protected]))([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])
aws-cdk:
specifier: ^2.173.4
version: 2.174.1
aws-cdk-lib:
specifier: ^2.173.4
version: 2.174.1([email protected])
constructs:
specifier: ^10.4.2
version: 10.4.2
esbuild:
specifier: ^0.24.2
version: 0.24.2
tsx:
specifier: ^4.19.2
version: 4.19.2
typescript:
specifier: ^5.7.2
version: 5.7.2`
);
const lockFileContents =
await pnpmLockFileReader.getLockFileContentsFromCwd();
assert.deepEqual(lockFileContents, { dependencies: [] });
});
});
Loading

0 comments on commit 2dab201

Please sign in to comment.