Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE] feat: inital ci-cd workflow #427

Merged
merged 5 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
30 changes: 30 additions & 0 deletions packages/blueprints/blueprint-builder/src/blueprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import devEnvPackage from '@amazon-codecatalyst/blueprint-component.dev-environm
import envPackage from '@amazon-codecatalyst/blueprint-component.environments/package.json';
import { SourceRepository, SourceFile, StaticAsset, File } from '@amazon-codecatalyst/blueprint-component.source-repositories';
import sourceReposPackage from '@amazon-codecatalyst/blueprint-component.source-repositories/package.json';
import { Workflow, WorkflowBuilder } from '@amazon-codecatalyst/blueprint-component.workflows';
import workflowsPackage from '@amazon-codecatalyst/blueprint-component.workflows/package.json';
import cliPackage from '@amazon-codecatalyst/blueprint-util.cli/package.json';
import { ProjenBlueprint, ProjenBlueprintOptions } from '@amazon-codecatalyst/blueprint-util.projen-blueprint';
Expand All @@ -15,6 +16,7 @@ import {
} from '@amazon-codecatalyst/blueprints.blueprint';
import baseBlueprintPackage from '@amazon-codecatalyst/blueprints.blueprint/package.json';
import * as decamelize from 'decamelize';
import { buildReleaseWorkflow } from './build-release-workflow';
import defaults from './defaults.json';

devEnvPackage.version;
Expand Down Expand Up @@ -63,6 +65,19 @@ export interface Options extends ParentOptions {
* @validationMessage This contains characters that are not allowed in NPM package names.
*/
blueprintPackageName?: string;

/**
* Generate a release workflow?
* If this is set, the blueprint will generate a release workflow. On push to main, a workflow will release this blueprint into your codecatalyst space.
*/
releaseWorkflow?: boolean;

/**
* Include a publishing step in the release workflow?
* If this is set, the generated release workflow will contain a publishing action.
* @hidden
*/
includePublishingAction?: boolean;
};
}

Expand Down Expand Up @@ -149,6 +164,10 @@ export class Blueprint extends ParentBlueprint {

// copy-paste additional code over it
StaticAsset.findAll().forEach(asset => {
if (asset.path() === 'release.sh') {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

wait huh? dont copy release.sh?

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed

return;
}

new File(repository, asset.path(), asset.content());
});

Expand Down Expand Up @@ -217,6 +236,17 @@ export class Blueprint extends ParentBlueprint {
],
},
});

if (options.advancedSettings.releaseWorkflow) {
const releaseWorkflow = new WorkflowBuilder(this);
new Workflow(
this,
repository,
buildReleaseWorkflow(releaseWorkflow, repository, {
includePublishStep: options.advancedSettings.includePublishingAction,
}).getDefinition(),
);
}
}

synth(): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { SourceFile, SourceRepository, SubstitionAsset } from '@amazon-codecatalyst/blueprint-component.source-repositories';
import { TriggerType, WorkflowBuilder } from '@amazon-codecatalyst/blueprint-component.workflows';

export function buildReleaseWorkflow(
workflow: WorkflowBuilder,
repository: SourceRepository,
options?: { includePublishStep?: boolean },
): WorkflowBuilder {
const publishingEnabled = options?.includePublishStep ?? true;

workflow.setName('blueprint-release');
const RELEASE_COMMIT_PREFIX = 'chore(release):';
const BUILD_ARTIFACT_NAME = 'codebase';

const releaseScript = new SubstitionAsset('release.sh');
new SourceFile(repository, 'release.sh', releaseScript.substitute({ commitPrefix: RELEASE_COMMIT_PREFIX }));

workflow.addBranchTrigger(['main']);
workflow.addTrigger({
Type: TriggerType.MANUAL,
});
workflow.addBuildAction({
actionName: 'check_commit',
input: {
Sources: ['WorkflowSource'],
},
output: {
Variables: ['IS_RELEASE_COMMIT'],
},
steps: [
'TRIGGER_COMMIT_ID=$CATALYST_EVENT_SHA',
'COMMIT_MESSAGE="$(git log -n 1 $TRIGGER_COMMIT_ID --oneline)"',
`RELEASE_PREFIX='${RELEASE_COMMIT_PREFIX}'`,
'IS_RELEASE_COMMIT=false',
'if grep -q "$RELEASE_PREFIX" <<< "$COMMIT_MESSAGE"; then echo \'this is a release commit\' && IS_RELEASE_COMMIT=true; fi',
],
});
workflow.addBuildAction({
actionName: 'build_and_commit',
dependsOn: ['check_commit'],
input: {
Sources: ['WorkflowSource'],
Variables: {
IS_RELEASE_COMMIT: '${check_commit.IS_RELEASE_COMMIT}',
},
},
output: {
Artifacts: [
{
Name: BUILD_ARTIFACT_NAME,
Files: ['**/*'],
},
],
},
steps: ["if $IS_RELEASE_COMMIT; then echo 'This is a release commit, skipping'; else chmod +x release.sh && ./release.sh; fi"],
});

if (publishingEnabled) {
workflow.addPublishBlueprintAction({
actionName: 'publish_blueprint',
dependsOn: ['build_and_commit'],
inputs: {
Artifacts: [BUILD_ARTIFACT_NAME],
Variables: [
{
Name: 'IS_RELEASE_COMMIT',
Value: '${check_commit.IS_RELEASE_COMMIT}',
},
],
},
configuration: {
ArtifactPackagePath: 'dist/js/*.tgz',
PackageJSONPath: 'package.json',
InputArtifactName: BUILD_ARTIFACT_NAME,
TimeoutInSeconds: '120',
},
});
}

return workflow;
}
4 changes: 3 additions & 1 deletion packages/blueprints/blueprint-builder/src/defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"advancedSettings": {
"blueprintPackageName": "",
"license": "Apache-2.0",
"tags": ["first-label", "second-label"]
"tags": ["first-label", "second-label"],
"releaseWorkflow": true,
"includePublishingAction": true
}
}
35 changes: 35 additions & 0 deletions packages/blueprints/blueprint-builder/static-assets/release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# This script is used by the blueprint-release workflow to build
# the blueprint, bump its package version, and commit the version
# bump back into the repository.
echo "Installing dependencies..."
yum install -y rsync
npm install -g yarn
yarn

echo "Building blueprint..."
yarn build

echo "Bumping package version..."
yarn bump
NEW_VERSION=`jq -r .version package.json`
yarn blueprint:package

echo "Getting credentials..."
MI=`curl $AWS_CONTAINER_TOKEN_ENDPOINT`
ACCESS_KEY_ID=$(echo "$MI" | jq -r '.AccessKeyId')
SECRET_ACCESS_KEY=$(echo "$MI" | jq -r '.SecretAccessKey')
ORIGINAL_REMOTE=`git config --get remote.origin.url`
SOURCE_REPO_URL=`sed -e "s^//^//$ACCESS_KEY_ID:$SECRET_ACCESS_KEY@^" <<< $ORIGINAL_REMOTE`

echo "Configuring git..."
git remote set-url origin $SOURCE_REPO_URL
git config --global user.email "noreply@amazon.com"
git config --global user.name "Release Workflow"
git add .

echo "Committing changes..."
RELEASE_COMMIT_MESSAGE="{{commitPrefix}} release $NEW_VERSION"
git commit -m "$RELEASE_COMMIT_MESSAGE"

echo "Pushing to origin..."
git push origin HEAD:main
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Blueprint } from '@amazon-codecatalyst/blueprints.blueprint';
import { ActionDefiniton, ActionIdentifierAlias, ComputeConfiguration, InputsDefinition, getDefaultActionIdentifier } from './action';
import { WorkflowDefinition } from '../workflow/workflow';

export interface PublishBlueprintActionConfiguration {
ArtifactPackagePath: string;
PackageJSONPath: string;
InputArtifactName: string;
TimeoutInSeconds?: string;
}

export interface PublishBlueprintActionParameters {
actionName: string;
inputs: InputsDefinition;
configuration: PublishBlueprintActionConfiguration;
dependsOn?: string[];
computeName?: ComputeConfiguration;
}

export function addGenericPublishBlueprintAction(
params: PublishBlueprintActionParameters & {
blueprint: Blueprint;
workflow: WorkflowDefinition;
},
): string {
const { blueprint, inputs, dependsOn, computeName, configuration } = params;
const actionName = (params.actionName || 'PublishBlueprint').replace(new RegExp('-', 'g'), '_');

const publishBlueprintAction: ActionDefiniton = {
Identifier: getDefaultActionIdentifier(ActionIdentifierAlias.publishBlueprint, blueprint.context.environmentId),
Inputs: inputs,
DependsOn: dependsOn,
Compute: computeName,
Configuration: configuration,
};

params.workflow.Actions = params.workflow.Actions || {};
params.workflow.Actions[actionName] = publishBlueprintAction;

return actionName;
}
10 changes: 9 additions & 1 deletion packages/components/workflows/src/actions/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { BuildActionConfiguration } from './action-build';
import { CdkBootstrapActionConfiguration } from './action-cdk-bootstrap';
import { CdkDeployActionYamlOutput } from './action-cdk-deploy';
import { CfnDeployActionConfiguration } from './action-cfn-deploy';
import { PublishBlueprintActionConfiguration } from './action-publish-blueprint';
import { TestActionConfiguration } from './action-test-reports';
import { WorkflowEnvironment } from '../environment/workflow-environment';

Expand All @@ -16,6 +17,7 @@ export enum ActionIdentifierAlias {
test = 'test',
cdkDeploy = 'cdkDeploy',
cdkBootstrap = 'cdkBootstrap',
publishBlueprint = 'publishBlueprint',
}

const ACTION_IDENTIFIERS: { [key: string]: { default: string; prod: string } } = {
Expand All @@ -39,6 +41,10 @@ const ACTION_IDENTIFIERS: { [key: string]: { default: string; prod: string } } =
default: 'aws/cdk-bootstrap-gamma@v1',
prod: 'aws/cdk-bootstrap@v1',
},
publishBlueprint: {
default: 'aws/publish-blueprint-action@v1',
prod: 'aws/publish-blueprint-action@v1',
},
};

export function getDefaultActionIdentifier(alias: ActionIdentifierAlias, environmentIdentifier: string = 'default'): string | undefined {
Expand All @@ -51,7 +57,9 @@ type TypeSupportedActions =
| CfnDeployActionConfiguration
| TestActionConfiguration
| CdkDeployActionYamlOutput
| CdkBootstrapActionConfiguration;
| CdkBootstrapActionConfiguration
| PublishBlueprintActionConfiguration;

export interface ActionDefiniton {
Identifier?: string;
Compute?: TypeSupportedCompute | string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { addGenericCdkBootstrapAction, CdkBootstrapActionParameters } from '../a
import { addGenericCdkDeployAction, CdkDeployActionParameters } from '../actions/action-cdk-deploy';
import { addGenericCloudFormationCleanupAction, CfnCleanupActionParameters } from '../actions/action-cfn-cleanup';
import { addGenericCloudFormationDeployAction, CfnDeployActionParameters } from '../actions/action-cfn-deploy';
import { addGenericPublishBlueprintAction, PublishBlueprintActionParameters } from '../actions/action-publish-blueprint';
import { addGenericTestReports, TestReportActionParameters } from '../actions/action-test-reports';

export class WorkflowBuilder {
Expand Down Expand Up @@ -99,4 +100,12 @@ export class WorkflowBuilder {
workflow: this.definition,
});
}

addPublishBlueprintAction(configuration: PublishBlueprintActionParameters) {
addGenericPublishBlueprintAction({
...configuration,
blueprint: this.blueprint,
workflow: this.definition,
});
}
}