Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
3 changes: 3 additions & 0 deletions .projen/tasks.json

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

14 changes: 14 additions & 0 deletions .projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AdcPublishing } from './projenrc/adc-publishing';
import { BundleCli } from './projenrc/bundle';
import { CodeCovWorkflow } from './projenrc/codecov';
import { ESLINT_RULES } from './projenrc/eslint';
import { InsertTaskStep } from './projenrc/insert-task-step';
import { IssueLabeler } from './projenrc/issue-labeler';
import { JsiiBuild } from './projenrc/jsii';
import { RecordPublishingTimestamp } from './projenrc/record-publishing-timestamp';
Expand Down Expand Up @@ -374,11 +375,24 @@ new JsiiBuild(cloudAssemblySchema, {
(() => {
cloudAssemblySchema.preCompileTask.exec('tsx projenrc/update.ts');

// This file will be generated at release time. It needs to be gitignored or it will
// fail projen's "no tamper" check, which means it must also be generated every build time.
cloudAssemblySchema.preCompileTask.exec('[[ -f cli-version.json ]] || echo \'{ "version": "" }\' > cli-version.json');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not call the same copy-cli-version-to-assembly.task.ts script and have it create the empty file in case the version is 0.0.0? Just trying to minimize bash.

cloudAssemblySchema.gitignore.addPatterns('cli-version.json');

cloudAssemblySchema.addPackageIgnore('*.ts');
cloudAssemblySchema.addPackageIgnore('!*.d.ts');
cloudAssemblySchema.addPackageIgnore('** /scripts');
})();

new InsertTaskStep(repoProject, {
taskName: 'release',
insertSteps: [
{ exec: 'ts-node projenrc/copy-cli-version-to-assembly.task.ts' },
],
beforeExec: 'yarn workspaces run build',
Copy link
Contributor

@iliapolo iliapolo Mar 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest making the necessary changes to InsertTaskStep so that invoking it eventually looks like this:

Suggested change
beforeExec: 'yarn workspaces run build',
afterExec: 'yarn workspaces run bump',

Since the bump task is our "dependency" here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had that first but I find a "before" much more natural for an "insert" operation.

Also, yes it has to be after bump, but it also equally has to be before build, so both make sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had that first but I find a "before" much more natural for an "insert" operation.

hh so call it AppendTaskStep :)

Its fine. just a personal preference.

});

//////////////////////////////////////////////////////////////////////

const cloudFormationDiff = configureProject(
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/cloud-assembly-schema/.gitignore

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

3 changes: 3 additions & 0 deletions packages/@aws-cdk/cloud-assembly-schema/.projen/tasks.json

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

23 changes: 22 additions & 1 deletion packages/@aws-cdk/cloud-assembly-schema/lib/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ import * as integ from './integ-tests';
// see exec.ts#createAssembly
export const VERSION_MISMATCH: string = 'Cloud assembly schema version mismatch';

/**
* CLI version is created at build and release time
*
* It needs to be .gitignore'd, otherwise the projen 'no uncommitted
* changes' self-check will fail, which means it needs to be generated
* at build time if it doesn't already exist.
*/
import CLI_VERSION = require('../cli-version.json');

import ASSETS_SCHEMA = require('../schema/assets.schema.json');

import ASSEMBLY_SCHEMA = require('../schema/cloud-assembly.schema.json');
Expand Down Expand Up @@ -141,6 +150,14 @@ export class Manifest {
return `${SCHEMA_VERSION.revision}.0.0`;
}

/**
* Return the CLI version that supports this Cloud Assembly Schema version
*/
public static cliVersion(): string | undefined {
const version = CLI_VERSION.version;
return version ? version : undefined;
}

/**
* Deprecated
* @deprecated use `saveAssemblyManifest()`
Expand Down Expand Up @@ -216,7 +233,11 @@ export class Manifest {
schema: jsonschema.Schema,
preprocess?: (obj: any) => any,
) {
let withVersion = { ...manifest, version: Manifest.version() };
let withVersion = {
...manifest,
version: Manifest.version(),
minimumCliVersion: Manifest.cliVersion(),
} satisfies assembly.AssemblyManifest;
Manifest.validate(withVersion, schema);
if (preprocess) {
withVersion = preprocess(withVersion);
Expand Down
31 changes: 29 additions & 2 deletions packages/@aws-cdk/cloud-assembly-schema/test/manifest.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-require-imports */
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
Expand Down Expand Up @@ -31,10 +32,36 @@ test('manifest save', () => {

const saved = JSON.parse(fs.readFileSync(manifestFile, { encoding: 'utf-8' }));

expect(saved).toEqual({
expect(saved).toEqual(expect.objectContaining({
...assemblyManifest,
version: Manifest.version(), // version is forced
});
}));
});

test('manifest contains minimum CLI version', () => {
const outdir = fs.mkdtempSync(path.join(os.tmpdir(), 'schema-tests'));
const manifestFile = path.join(outdir, 'manifest.json');

// This relies on the fact that the cli JSON version file is `require()`d,
// and that the 'require' below will find the same object in the module cache.
const cliVersionFile = require(`${__dirname}/../cli-version.json`);
cliVersionFile.version = '9.9.9';
try {
const assemblyManifest: AssemblyManifest = {
version: 'version',
runtime: {
libraries: { lib1: '1.2.3' },
},
};

Manifest.saveAssemblyManifest(assemblyManifest, manifestFile);

const saved = JSON.parse(fs.readFileSync(manifestFile, { encoding: 'utf-8' }));

expect(saved.minimumCliVersion).toEqual('9.9.9');
} finally {
cliVersionFile.version = '';
}
});

test('assumeRoleAdditionalOptions.RoleArn is validated in stack artifact', () => {
Expand Down
19 changes: 19 additions & 0 deletions projenrc/copy-cli-version-to-assembly.task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { promises as fs } from 'fs';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just FYI, not sure which is better

Suggested change
import { promises as fs } from 'fs';
import * fs from 'fs/promises';


/**
* Copy the version from the CLI into the `@aws-cdk/cloud-assembly-schema` package at release time.
*/
async function main() {
const cliVersion = JSON.parse(await fs.readFile('packages/aws-cdk/package.json', 'utf8')).version;

if (cliVersion !== '0.0.0') {
await fs.writeFile('packages/@aws-cdk/cloud-assembly-schema/cli-version.json', JSON.stringify({ version: cliVersion }), 'utf8');
}
}

main().catch(e => {
// this is effectively a mini-cli
// eslint-disable-next-line no-console
console.error(e);
process.exitCode = 1;
});
29 changes: 29 additions & 0 deletions projenrc/insert-task-step.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Component, Project, TaskStep } from 'projen';

export interface InsertTaskStepProps {
readonly taskName: string;
readonly insertSteps: TaskStep[];
readonly beforeExec: string;
}

export class InsertTaskStep extends Component {
constructor(project: Project, private props: InsertTaskStepProps) {
super(project);
}

preSynthesize() {
const task = this.project.tasks.tryFind(this.props.taskName);
if (!task) {
throw new Error(`Did not find task ${this.props.taskName}`);
}

// Find the bump task, and do the CLI version copy straight after
const bumpIx = task.steps.findIndex(s => s.exec === this.props.beforeExec);
if (bumpIx === -1) {
throw new Error(`Did not find step: ${this.props.beforeExec}`);
}

// Accessing internals like a dirty boi
(task as any)._steps.splice(bumpIx, 0, ...this.props.insertSteps);
}
}
Loading