diff --git a/packages/@aws-cdk/cli-lib-alpha/.eslintrc.js b/packages/@aws-cdk/cli-lib-alpha/.eslintrc.js index b284f20df26e9..f2bee0ec3d188 100644 --- a/packages/@aws-cdk/cli-lib-alpha/.eslintrc.js +++ b/packages/@aws-cdk/cli-lib-alpha/.eslintrc.js @@ -1,5 +1,6 @@ const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +baseConfig.ignorePatterns.push('**/*.template.ts'); baseConfig.rules['import/no-extraneous-dependencies'] = ['error', { devDependencies: true, peerDependencies: true } ]; baseConfig.rules['import/order'] = 'off'; diff --git a/packages/@aws-cdk/cli-lib-alpha/.gitignore b/packages/@aws-cdk/cli-lib-alpha/.gitignore index 573a12d965554..e728027e0ff5f 100644 --- a/packages/@aws-cdk/cli-lib-alpha/.gitignore +++ b/packages/@aws-cdk/cli-lib-alpha/.gitignore @@ -2,7 +2,7 @@ *.js.map *.d.ts *.gz -!lib/init-templates/**/javascript/**/* +lib/init-templates/ node_modules dist .jsii diff --git a/packages/@aws-cdk/cli-lib-alpha/build-tools/copy-cli-resources.sh b/packages/@aws-cdk/cli-lib-alpha/build-tools/copy-cli-resources.sh new file mode 100755 index 0000000000000..d04e7fe939da4 --- /dev/null +++ b/packages/@aws-cdk/cli-lib-alpha/build-tools/copy-cli-resources.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -euo pipefail + +aws_cdk="$1" + +# Copy all resources that aws_cdk/generate.sh produced, and some othersCall the generator for the +cp $aws_cdk/build-info.json ./ +cp -R $aws_cdk/lib/init-templates ./lib/ + +mkdir -p ./lib/api/bootstrap/ && cp $aws_cdk/lib/api/bootstrap/bootstrap-template.yaml ./lib/api/bootstrap/ +cp $aws_cdk/lib/index_bg.wasm ./lib/ +cp $aws_cdk/db.json.gz ./ diff --git a/packages/@aws-cdk/cli-lib-alpha/package.json b/packages/@aws-cdk/cli-lib-alpha/package.json index 49a48e313c633..a72dc5f7c736e 100644 --- a/packages/@aws-cdk/cli-lib-alpha/package.json +++ b/packages/@aws-cdk/cli-lib-alpha/package.json @@ -6,6 +6,9 @@ "main": "lib/main.js", "types": "lib/index.d.ts", "jsii": { + "excludeTypescript": [ + "**/*.template.ts" + ], "outdir": "dist", "targets": { "dotnet": { @@ -52,7 +55,7 @@ "build+test+package": "yarn build+test && yarn package", "bundle": "esbuild --bundle lib/index.ts --target=node18 --platform=node --external:fsevents --minify-whitespace --outfile=lib/main.js", "compat": "cdk-compat", - "gen": "../../../packages/aws-cdk/generate.sh", + "gen": "build-tools/copy-cli-resources.sh ../../../packages/aws-cdk", "lint": "cdk-lint", "package": "cdk-package", "pkglint": "pkglint -f", @@ -63,9 +66,6 @@ "cdk-build": { "post": [ "yarn attribute", - "mkdir -p ./lib/api/bootstrap/ && cp ../../aws-cdk/lib/api/bootstrap/bootstrap-template.yaml ./lib/api/bootstrap/", - "cp ../../../node_modules/cdk-from-cfn/index_bg.wasm ./lib/", - "cp ../../../node_modules/@aws-cdk/aws-service-spec/db.json.gz ./", "yarn bundle", "node ./lib/main.js >/dev/null 2>/dev/null ): Record { return { - ...NEW_PROJECT_CONTEXT, + ...currentlyRecommendedAwsCdkLibFlags(), ...this.legacyContext, ...additionalContext, @@ -432,3 +432,13 @@ export const DEFAULT_SYNTH_OPTIONS = { CDK_INTEG_SUBNET_ID: 'subnet-0dff1a399d8f6f92c', }, }; + +/** + * Return the currently recommended flags for `aws-cdk-lib`. + * + * These have been built into the CLI at build time. + */ +export function currentlyRecommendedAwsCdkLibFlags() { + const recommendedFlagsFile = path.join(__dirname, '..', 'recommended-feature-flags.json'); + return JSON.parse(fs.readFileSync(recommendedFlagsFile, { encoding: 'utf-8' })); +} diff --git a/packages/@aws-cdk/integ-runner/package.json b/packages/@aws-cdk/integ-runner/package.json index 3b96e1f8d8194..482510d735fcb 100644 --- a/packages/@aws-cdk/integ-runner/package.json +++ b/packages/@aws-cdk/integ-runner/package.json @@ -8,6 +8,7 @@ "integ-runner": "bin/integ-runner" }, "scripts": { + "gen": "./build-tools/generate.sh", "build": "cdk-build", "lint": "cdk-lint", "package": "cdk-package", diff --git a/packages/aws-cdk-lib/cx-api/build-tools/flag-report.ts b/packages/aws-cdk-lib/cx-api/build-tools/flag-report.ts index 654684c091d77..00477c8833876 100644 --- a/packages/aws-cdk-lib/cx-api/build-tools/flag-report.ts +++ b/packages/aws-cdk-lib/cx-api/build-tools/flag-report.ts @@ -15,6 +15,9 @@ async function main() { diff: changedFlags(), migratejson: migrateJson(), }); + + // Write to the package root + await updateRecommendedFlagsFile(path.join(__dirname, '..', '..', 'recommended-feature-flags.json')); } function flagsTable() { @@ -117,7 +120,7 @@ function oldBehavior(flag: FlagInfo): string | undefined { function recommendedJson() { return [ '```json', - JSON.stringify({ context: feats.NEW_PROJECT_CONTEXT }, undefined, 2), + JSON.stringify({ context: feats.CURRENTLY_RECOMMENDED_FLAGS }, undefined, 2), '```', ].join('\n'); } @@ -206,6 +209,10 @@ async function updateMarkdownFile(filename: string, sections: Record x !== 0) ?? 0; } diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index 72ab65805054f..2992b611be55a 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -1317,10 +1317,13 @@ export const CURRENT_VERSION_EXPIRED_FLAGS: string[] = Object.entries(FLAGS) * Add a flag in here (typically with the value `true`), to enable * backwards-breaking behavior changes only for new projects. New projects * generated through `cdk init` will include these flags in their generated + * project config. * * Tests must cover the default (disabled) case and the future (enabled) case. + * + * Going forward, this should *NOT* be consumed directly anymore. */ -export const NEW_PROJECT_CONTEXT = Object.fromEntries( +export const CURRENTLY_RECOMMENDED_FLAGS = Object.fromEntries( Object.entries(FLAGS) .filter(([_, flag]) => flag.recommendedValue !== flag.defaults?.[CURRENT_MV] && flag.introducedIn[CURRENT_MV]) .map(([name, flag]) => [name, flag.recommendedValue]), @@ -1352,10 +1355,10 @@ export function futureFlagDefault(flag: string): boolean { /** @deprecated use CURRENT_VERSION_EXPIRED_FLAGS instead */ export const FUTURE_FLAGS_EXPIRED = CURRENT_VERSION_EXPIRED_FLAGS; -/** @deprecated use NEW_PROJECT_CONTEXT instead */ -export const FUTURE_FLAGS = Object.fromEntries(Object.entries(NEW_PROJECT_CONTEXT) +/** @deprecated do not use at all! */ +export const FUTURE_FLAGS = Object.fromEntries(Object.entries(CURRENTLY_RECOMMENDED_FLAGS) .filter(([_, v]) => typeof v === 'boolean')); -/** @deprecated use NEW_PROJECT_CONTEXT instead */ -export const NEW_PROJECT_DEFAULT_CONTEXT = Object.fromEntries(Object.entries(NEW_PROJECT_CONTEXT) +/** @deprecated do not use at all! */ +export const NEW_PROJECT_DEFAULT_CONTEXT = Object.fromEntries(Object.entries(CURRENTLY_RECOMMENDED_FLAGS) .filter(([_, v]) => typeof v !== 'boolean')); diff --git a/packages/aws-cdk-lib/cx-api/lib/private/flag-modeling.ts b/packages/aws-cdk-lib/cx-api/lib/private/flag-modeling.ts index ba9389cf45a9d..55279650d075e 100644 --- a/packages/aws-cdk-lib/cx-api/lib/private/flag-modeling.ts +++ b/packages/aws-cdk-lib/cx-api/lib/private/flag-modeling.ts @@ -30,7 +30,7 @@ export interface FlagInfoBase { /** Version number the flag was introduced in each version line. `undefined` means flag does not exist in that line. */ readonly introducedIn: { v1?: string; v2?: string }; /** Default value, if flag is unset by user. Adding a flag with a default may not change behavior after GA! */ - readonly defaults?: { v2?: any }; + readonly defaults?: { v1?: any; v2?: any }; /** Default in new projects */ readonly recommendedValue: any; }; diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 4c31ed54c5056..e6bd442c3bcc4 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -509,6 +509,7 @@ "./pipelines/.warnings.jsii.js": "./pipelines/.warnings.jsii.js", "./pipelines/lib/helpers-internal": "./pipelines/lib/helpers-internal/index.js", "./pipelines/package.json": "./pipelines/package.json", + "./recommended-feature-flags.json": "./recommended-feature-flags.json", "./region-info": "./region-info/index.js", "./triggers": "./triggers/index.js" }, diff --git a/packages/aws-cdk-lib/recommended-feature-flags.json b/packages/aws-cdk-lib/recommended-feature-flags.json new file mode 100644 index 0000000000000..30a08f65a928c --- /dev/null +++ b/packages/aws-cdk-lib/recommended-feature-flags.json @@ -0,0 +1,63 @@ +{ + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, + "@aws-cdk/aws-eks:nodegroupNameAttribute": true, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true +} \ No newline at end of file diff --git a/packages/aws-cdk/.gitignore b/packages/aws-cdk/.gitignore index b4917729cbcb0..d0b5e0796af72 100644 --- a/packages/aws-cdk/.gitignore +++ b/packages/aws-cdk/.gitignore @@ -4,6 +4,7 @@ !lib/init-templates/**/javascript/**/* lib/init-templates/**/*.hook.js lib/init-templates/**/*.hook.d.ts +lib/init-templates/.*.json node_modules dist @@ -40,5 +41,6 @@ test/integ/cli/*.d.ts junit.xml + lib/**/*.wasm db.json.gz diff --git a/packages/aws-cdk/generate.sh b/packages/aws-cdk/generate.sh index 4d9d43a954b6b..9a74b49240b88 100755 --- a/packages/aws-cdk/generate.sh +++ b/packages/aws-cdk/generate.sh @@ -14,3 +14,12 @@ cat > build-info.json < lib/init-templates/.init-version.json + +# Copy the recommended-feature-flags.json file out from aws-cdk-lib. +path=$(node -p 'require.resolve("aws-cdk-lib/recommended-feature-flags.json")') +cp $path lib/init-templates/.recommended-feature-flags.json \ No newline at end of file diff --git a/packages/aws-cdk/lib/init-templates/lib/typescript/tsconfig.json b/packages/aws-cdk/lib/init-templates/lib/typescript/tsconfig.json index aaa7dc510f1d2..1fa2eb4d6dbf9 100644 --- a/packages/aws-cdk/lib/init-templates/lib/typescript/tsconfig.json +++ b/packages/aws-cdk/lib/init-templates/lib/typescript/tsconfig.json @@ -26,6 +26,6 @@ }, "exclude": [ "node_modules", - "cdk.out" + "cdk.out", ] } diff --git a/packages/aws-cdk/lib/init.ts b/packages/aws-cdk/lib/init.ts index d119f3b78cc0a..c6801b0229554 100644 --- a/packages/aws-cdk/lib/init.ts +++ b/packages/aws-cdk/lib/init.ts @@ -1,6 +1,5 @@ import * as childProcess from 'child_process'; import * as path from 'path'; -import * as cxapi from '@aws-cdk/cx-api'; import * as chalk from 'chalk'; import * as fs from 'fs-extra'; import { invokeBuiltinHooks } from './init-hooks'; @@ -126,6 +125,7 @@ export class InitTemplate { const projectInfo: ProjectInfo = { name: decamelize(path.basename(path.resolve(targetDirectory))), stackName, + versions: await loadInitVersions(), }; const sourceDirectory = path.join(this.basePath, language); @@ -173,11 +173,9 @@ export class InitTemplate { } private expand(template: string, language: string, project: ProjectInfo) { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const manifest = require(path.join(rootDir(), 'package.json')); - const MATCH_VER_BUILD = /\+[a-f0-9]+$/; // Matches "+BUILD" in "x.y.z-beta+BUILD" - const cdkVersion = manifest.version.replace(MATCH_VER_BUILD, ''); - let constructsVersion = manifest.devDependencies.constructs.replace(MATCH_VER_BUILD, ''); + const cdkVersion = project.versions['aws-cdk-lib']; + let constructsVersion = project.versions.constructs; + switch (language) { case 'java': case 'csharp': @@ -222,7 +220,7 @@ export class InitTemplate { const config = await fs.readJson(cdkJson); config.context = { ...config.context, - ...cxapi.NEW_PROJECT_CONTEXT, + ...await currentlyRecommendedAwsCdkLibFlags(), }; await fs.writeJson(cdkJson, config, { spaces: 2 }); @@ -248,6 +246,8 @@ interface ProjectInfo { /** The value used for %name% */ readonly name: string; readonly stackName?: string; + + readonly versions: Versions; } export async function availableInitTemplates(): Promise { @@ -471,3 +471,41 @@ async function execute(cmd: string, args: string[], { cwd }: { cwd: string }) { }); }); } + +interface Versions { + ['aws-cdk-lib']: string; + constructs: string; +} + +/** + * Return the 'aws-cdk-lib' version we will init + * + * This has been built into the CLI at build time. + */ +async function loadInitVersions(): Promise { + const recommendedFlagsFile = path.join(__dirname, './init-templates/.init-version.json'); + const contents = JSON.parse(await fs.readFile(recommendedFlagsFile, { encoding: 'utf-8' })); + + const ret = { + 'aws-cdk-lib': contents['aws-cdk-lib'], + 'constructs': contents.constructs, + }; + for (const [key, value] of Object.entries(ret)) { + /* istanbul ignore next */ + if (!value) { + throw new Error(`Missing init version from ${recommendedFlagsFile}: ${key}`); + } + } + + return ret; +} + +/** + * Return the currently recommended flags for `aws-cdk-lib`. + * + * These have been built into the CLI at build time. + */ +export async function currentlyRecommendedAwsCdkLibFlags() { + const recommendedFlagsFile = path.join(__dirname, './init-templates/.recommended-feature-flags.json'); + return JSON.parse(await fs.readFile(recommendedFlagsFile, { encoding: 'utf-8' })); +} diff --git a/packages/aws-cdk/test/init.test.ts b/packages/aws-cdk/test/init.test.ts index 02a94f4addf70..2ef2582f1b63c 100644 --- a/packages/aws-cdk/test/init.test.ts +++ b/packages/aws-cdk/test/init.test.ts @@ -2,7 +2,7 @@ import * as os from 'os'; import * as path from 'path'; import * as cxapi from '@aws-cdk/cx-api'; import * as fs from 'fs-extra'; -import { availableInitLanguages, availableInitTemplates, cliInit, printAvailableTemplates } from '../lib/init'; +import { availableInitLanguages, availableInitTemplates, cliInit, currentlyRecommendedAwsCdkLibFlags, printAvailableTemplates } from '../lib/init'; describe('constructs version', () => { cliTest('create a TypeScript library project', async (workDir) => { @@ -211,8 +211,36 @@ describe('constructs version', () => { expect(await fs.pathExists(path.join(workDir, 'bin'))).toBeTruthy(); }); - test('verify "future flags" are added to cdk.json', async () => { + cliTest('CLI uses recommended feature flags from data file to initialize context', async (workDir) => { + const recommendedFlagsFile = path.join(__dirname, '..', 'lib', 'init-templates', '.recommended-feature-flags.json'); + await withReplacedFile(recommendedFlagsFile, JSON.stringify({ banana: 'yellow' }), () => cliInit({ + type: 'app', + language: 'typescript', + canUseNetwork: false, + generateOnly: true, + workDir, + })); + + const cdkFile = await fs.readJson(path.join(workDir, 'cdk.json')); + expect(cdkFile.context).toEqual({ banana: 'yellow' }); + }); + + cliTest('CLI uses init versions file to initialize template', async (workDir) => { + const recommendedFlagsFile = path.join(__dirname, '..', 'lib', 'init-templates', '.init-version.json'); + await withReplacedFile(recommendedFlagsFile, JSON.stringify({ 'aws-cdk-lib': '100.1.1', 'constructs': '^200.2.2' }), () => cliInit({ + type: 'app', + language: 'typescript', + canUseNetwork: false, + generateOnly: true, + workDir, + })); + const packageJson = await fs.readJson(path.join(workDir, 'package.json')); + expect(packageJson.dependencies['aws-cdk-lib']).toEqual('100.1.1'); + expect(packageJson.dependencies.constructs).toEqual('^200.2.2'); + }); + + test('verify "future flags" are added to cdk.json', async () => { for (const templ of await availableInitTemplates()) { for (const lang of templ.languages) { await withTempDir(async tmpDir => { @@ -231,9 +259,10 @@ describe('constructs version', () => { const config = await fs.readJson(path.join(tmpDir, 'cdk.json')); const context = config.context || {}; + const recommendedFlags = await currentlyRecommendedAwsCdkLibFlags(); for (const [key, actual] of Object.entries(context)) { - expect(key in cxapi.NEW_PROJECT_CONTEXT).toBeTruthy(); - expect(cxapi.NEW_PROJECT_CONTEXT[key]).toEqual(actual); + expect(key in recommendedFlags).toBeTruthy(); + expect(recommendedFlags[key]).toEqual(actual); } // assert that expired future flags are not part of the cdk.json @@ -307,3 +336,13 @@ async function recursiveListFiles(rdir: string): Promise { } } } + +async function withReplacedFile(fileName: string, contents: any, cb: () => Promise): Promise { + const oldContents = await fs.readFile(fileName, 'utf8'); + await fs.writeFile(fileName, contents); + try { + await cb(); + } finally { + await fs.writeFile(fileName, oldContents); + } +} diff --git a/tools/@aws-cdk/pkglint/lib/rules.ts b/tools/@aws-cdk/pkglint/lib/rules.ts index 531dd1d18cc95..849c17f4ce067 100644 --- a/tools/@aws-cdk/pkglint/lib/rules.ts +++ b/tools/@aws-cdk/pkglint/lib/rules.ts @@ -1411,7 +1411,7 @@ export class PackageInJsiiPackageNoRuntimeDeps extends ValidationRule { public readonly name = 'lambda-packages-no-runtime-deps'; public validate(pkg: PackageJson) { - if (!isJSII(pkg)) { return; } + if (!isJSII(pkg) || pkg.packageName === '@aws-cdk/cli-lib-alpha') { return; } for (const inner of findInnerPackages(pkg.packageRoot)) { const innerPkg = PackageJson.fromDirectory(inner);