Skip to content

Commit

Permalink
Merge pull request #517 from michaellandi/feature/launch-mappings
Browse files Browse the repository at this point in the history
feat(launch-blueprint): add configuration and environment mappings
  • Loading branch information
michaellandi authored Mar 27, 2024
2 parents 31f05f9 + 2143e17 commit 2fecf84
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 118 deletions.
Binary file not shown.
8 changes: 8 additions & 0 deletions packages/blueprints/launch-blueprint/.projen/deps.json

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

9 changes: 8 additions & 1 deletion packages/blueprints/launch-blueprint/.projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,21 @@ const project = new ProjenBlueprint({
},
copyrightOwner: 'blueprints',
deps: [
'yaml',
'@amazon-codecatalyst/blueprints.blueprint',
'projen',
'@amazon-codecatalyst/blueprint-component.workflows',
'@amazon-codecatalyst/blueprint-component.source-repositories',
'@amazon-codecatalyst/blueprint-component.environments',
],

devDeps: ['ts-node', 'typescript', '@amazon-codecatalyst/blueprint-util.projen-blueprint', '@amazon-codecatalyst/blueprint-util.cli'],
devDeps: [
'ts-node',
'typescript',
'@amazon-codecatalyst/blueprint-util.projen-blueprint',
'@amazon-codecatalyst/blueprint-util.cli',
'@types/yaml',
],
keywords: ['blueprint-publisher', 'external-blueprint', 'blueprint'],
homepage: '',
});
Expand Down
6 changes: 4 additions & 2 deletions packages/blueprints/launch-blueprint/package.json

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

169 changes: 67 additions & 102 deletions packages/blueprints/launch-blueprint/src/blueprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@ import * as fs from 'fs';
import path from 'path';
import { EnvironmentDefinition, AccountConnection, Role, Environment } from '@amazon-codecatalyst/blueprint-component.environments';
import { SourceRepository } from '@amazon-codecatalyst/blueprint-component.source-repositories';
import {
BuildActionParameters,
Workflow,
WorkflowDefinition,
addGenericBranchTrigger,
addGenericBuildAction,
makeEmptyWorkflow,
} from '@amazon-codecatalyst/blueprint-component.workflows';
import { ConnectionDefinition, InputVariable, WorkflowDefinition, WorkflowEnvironment } from '@amazon-codecatalyst/blueprint-component.workflows';
import { Blueprint as ParentBlueprint, Options as ParentOptions, Selector, Tuple } from '@amazon-codecatalyst/blueprints.blueprint';
import * as yaml from 'yaml';
// eslint-disable-next-line import/no-extraneous-dependencies
import defaults from './defaults.json';

Expand All @@ -29,7 +23,10 @@ export interface Options extends ParentOptions {
*/
destinationRepositoryName: Selector<SourceRepository | string>;

environment: EnvironmentDefinition<{
/**
* @showName readOnly
*/
environments: EnvironmentDefinition<{
/**
* An AWS account connection is required by the project workflow to deploy to aws.
* @displayName AWS account connection
Expand All @@ -42,29 +39,16 @@ export interface Options extends ParentOptions {
* @inlinePolicy ./inline-policy-deploy.json
* @trustPolicy ./trust-policy.json
*/
deployRole: Role<['codecatalyst*']>;
launchRole: Role<['codecatalyst*']>;
}>;
}>;
}>[];

options: Tuple<[string, string]>[];

/**
* Deployment options
* @collapsed true
*/
deployment: {
/**
* Steps to build and deploy
*/
buildSteps: string[];

/**
* Custom container image for build and deployment
*/
containerImage?: string;
};
}

const OPTIONS_PREFIX = 'LAUNCH_OPTIONS_';
const GIT_CLONE_TIMEOUT = 30_000;

/**
* This is the actual blueprint class.
* 1. This MUST be the only 'class' exported, as 'Blueprint'
Expand All @@ -91,93 +75,25 @@ export class Blueprint extends ParentBlueprint {
title: options.destinationRepositoryName,
});

// create environments
for (const environment of options.environments) {
new Environment(this, environment);
}

this.state = {
options,
repository,
};

if (options.deployment.buildSteps?.length) {
const workflowDefinition: WorkflowDefinition = {
...makeEmptyWorkflow(),
SchemaVersion: '1.0',
Name: 'launch',
};

addGenericBranchTrigger(workflowDefinition, ['main']);

addGenericBuildAction({
blueprint: this,
workflow: workflowDefinition,
actionName: 'source',
input: {
Sources: ['WorkflowSource'],
},
output: {
AutoDiscoverReports: {
Enabled: false,
ReportNamePrefix: 'rpt',
},
Artifacts: [
{
Name: 'src',
Files: ['**/*'],
},
],
},
steps: ['ls -la'],
});

const buildAction: BuildActionParameters & {
blueprint: ParentBlueprint;
workflow: WorkflowDefinition;
} = {
blueprint: this,
workflow: workflowDefinition,
actionName: 'deploy',
input: {
Sources: [],
Artifacts: ['src'],
},
output: {
AutoDiscoverReports: {
Enabled: false,
ReportNamePrefix: 'rpt',
},
},
steps: [...options.deployment.buildSteps],
environment: {
Name: options.environment.name || ' ',
Connections: [
{
Name: options.environment.awsAccountConnection?.name || ' ',
Role: options.environment.awsAccountConnection?.deployRole?.name || ' ',
},
],
},
};

if (options.deployment.containerImage) {
buildAction.container = {
Image: options.deployment.containerImage,
Registry: 'ECR',
};
}

addGenericBuildAction(buildAction);

// create an environment
new Environment(this, options.environment);
new Workflow(this, repository, workflowDefinition);
}
}

override synth(): void {
const pathToRepository = path.join(this.context.durableStoragePath, this.state.repository.title);

if (!fs.existsSync(pathToRepository)) {
cp.spawnSync('git', ['clone', '--depth', '1', this.state.options.sourceRepository, this.state.repository.title], {
cwd: this.context.durableStoragePath,
stdio: [0, 1, 1],
timeout: 30_000,
timeout: GIT_CLONE_TIMEOUT,
});

// remove .git - we have no use for it and the large number of objects
Expand All @@ -187,6 +103,55 @@ export class Blueprint extends ParentBlueprint {

fs.cpSync(pathToRepository, this.state.repository.path, { recursive: true });

//map options and environments to workflows
const workflowPath = path.join(this.state.repository.path, '.codecatalyst', 'workflows');
if (fs.existsSync(workflowPath)) {
const workflowFiles = fs.readdirSync(workflowPath);

//load each workflow from the cloned repository
for (const workflowFile of workflowFiles) {
const workflowFilePath = path.join(workflowPath, workflowFile);
const workflowYaml = fs.readFileSync(workflowFilePath).toString('utf-8');
const workflow = yaml.parse(workflowYaml) as WorkflowDefinition;
for (const actionName of Object.keys(workflow.Actions ?? [])) {
const action = workflow.Actions?.[actionName];

//set variables with options where applicable
const variables = action.Inputs?.Variables as InputVariable[] | undefined;
for (const variable of variables ?? []) {
if (variable?.Name?.startsWith(OPTIONS_PREFIX)) {
const optionName = (variable.Name as string).replace(OPTIONS_PREFIX, '');
const optionValue = this.state.options.options.find(option => option[0] == optionName)?.[1];
if (optionValue) {
variable.Value = optionValue?.toString();
}
}
}

//set action environments from options where applicable
const actionEnvironment = action.Environment as WorkflowEnvironment | undefined;
if (actionEnvironment?.Name) {
const environment = this.state.options.environments.find(env => env.name == actionEnvironment.Name) as EnvironmentDefinition<{
awsAccountConnection: AccountConnection<{
launchRole: Role<['codecatalyst*']>;
}>;
}>;
if (environment?.awsAccountConnection?.name) {
actionEnvironment.Connections = [
{
Name: environment.awsAccountConnection.name,
Role: environment.awsAccountConnection.launchRole?.name ?? 'No role selected',
} as ConnectionDefinition,
];
}
}
}

//overwrite existing workflow
fs.writeFileSync(workflowFilePath, yaml.stringify(workflow));
}
}

super.synth();
}
}
15 changes: 2 additions & 13 deletions packages/blueprints/launch-blueprint/src/defaults.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
{
"sourceRepository": "https://github.com/aws-solutions/qnabot-on-aws",
"destinationRepositoryName": "launch-with-codecatalyst",
"environment": {
"name": "default_environment",
"environmentType": "PRODUCTION"
},
"options": [

],
"deployment": {
"containerImage": "public.ecr.aws/amazonlinux/amazonlinux:2023",
"buildSteps": [
"yum install -y nodejs make git", "npm install", "npm run config", "npm run bootstrap", "npm run up"
]
}
"environments": [],
"options": []
}
11 changes: 11 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ __metadata:
"@amazon-codecatalyst/blueprint-util.projen-blueprint": ^0.3.82
"@amazon-codecatalyst/blueprints.blueprint": ^0.3.82
"@types/node": ^18
"@types/yaml": "*"
"@typescript-eslint/eslint-plugin": ^5
"@typescript-eslint/parser": ^5
eslint: ^8
Expand All @@ -377,6 +378,7 @@ __metadata:
standard-version: ^9
ts-node: ^10
typescript: ^4.x
yaml: "*"
languageName: unknown
linkType: soft

Expand Down Expand Up @@ -3747,6 +3749,15 @@ __metadata:
languageName: node
linkType: hard

"@types/yaml@npm:*":
version: 1.9.6
resolution: "@types/yaml@npm:1.9.6"
dependencies:
yaml: "*"
checksum: 3df74c9d4028641e2ed710b627ac533e97707713ac17c665df5cc477602f77925a3ceeaf5db76c7400ea42be114609bba990079907e89219ffd905a539997469
languageName: node
linkType: hard

"@types/yargs-parser@npm:*":
version: 21.0.0
resolution: "@types/yargs-parser@npm:21.0.0"
Expand Down

0 comments on commit 2fecf84

Please sign in to comment.